diff --git a/README.md b/README.md
index 8ea4c0a..7c5d632 100644
--- a/README.md
+++ b/README.md
@@ -1,143 +1,161 @@
+# π¦ PyStdf4
----
-
-# **pystdf4**
+**PyStdf4** is a modern, high-performance Python library for creating and parsing **STDF (Standard Test Data Format) v4** files, widely used in semiconductor testing and manufacturing.
-**An object-oriented Python library for parsing and generating STDF (Standard Test Data Format) files used in semiconductor testing and manufacturing.**
+It provides a clean, Pythonic interface that abstracts low-level binary handling, letting engineers focus on data modeling instead of byte-level details.
---
-## π§© Overview
+## π Overview
-**pystdf4** provides a modern, object-oriented interface for working with **STDF v4** files β the industry-standard binary data format for semiconductor test results.
-It supports both **reading** and **writing** of STDF files, with a clean Python API that abstracts low-level binary details into high-level Pythonic objects.
+With **PyStdf4**, you can efficiently:
----
+- Generate STDF v4 files using structured Python objects
+- Build records with a robust, extensible field/record system
+- Work with a high-performance dynamic byte buffer optimized for large datasets
+- Integrate seamlessly into automated test and analysis pipelines
-## π STDF4 Data Type Codes
-
-The **Standard Test Data Format (STDF)** specification defines compact type codes to describe how values are stored and interpreted within records.
-Each type code corresponds to a C data type, defining the byte layout and valid range of values.
-
-| Code | Description | C Type Specifier | Notes |
-| :------- | :-------------------------------------------- | :--------------- | :----------------------------------------------------------------------- |
-| `C*1` | Fixed-length character string (1 characters) | `char[1]` | Left-justified; pad with spaces if shorter than 12 characters. |
-| `C*n` | Variable-length character string | `char[]` | First byte stores the length (0β255). |
-| `C*f` | Variable-length string with external length | `char[]` | Length determined by another record field. |
-| `U*1` | 1-byte unsigned integer | `unsigned char` | Range: 0β255 |
-| `U*2` | 2-byte unsigned integer | `unsigned short` | Range: 0β65,535 |
-| `U*4` | 4-byte unsigned integer | `unsigned long` | Range: 0β4,294,967,295 |
-| `I*1` | 1-byte signed integer | `char` | Range: β128 to 127 |
-| `I*2` | 2-byte signed integer | `short` | Range: β32,768 to 32,767 |
-| `I*4` | 4-byte signed integer | `long` | Range: β2,147,483,648 to 2,147,483,647 |
-| `R*4` | 4-byte floating-point number (IEEE 754) | `float` | Standard single-precision float |
-| `R*8` | 8-byte floating-point number (IEEE 754) | `double` | Standard double-precision float |
-| `B*6` | Fixed-length binary field (6 bytes) | `char[6]` | Raw binary data |
-| `V*n` | Variable-type field | β | First byte = type code, followed by up to 255 bytes of data |
-| `B*n` | Variable-length binary field | `char[]` | First byte = byte count (0β255); data starts at LSB of second byte |
-| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count (0β65,535); unused bits in last byte = 0 |
-| `N*1` | Nibble (4-bit) data | `char` | Stores unsigned integers in 4-bit units; high nibble zeroed if odd count |
-| `kxTYPE` | Array of specified data type | `TYPE[]` | Length `k` determined by another record field (e.g. `kxU*2`) |
+> β οΈ STDF file **parsing (reader API)** is under active development.
---
-## π§± PyStdf4 Data Model
+## π§ͺ Example Usage
-The **PyStdf4 type system** provides a structured, extensible way to represent STDF data elements as Python classes.
-Each class encapsulates type conversion, byte parsing, and STDF serialization.
+### Installation
-```
-Field (ABC)
-βββ ScalarField[_PyT]
-β βββ U_1, U_2, U_4 # unsigned int
-β βββ I_1, I_2, I_4 # signed int
-β βββ R_4, R_8 # float
-β βββ C_1 # char β bytes
-β βββ B_1 # raw bytes
-βββ SequenceField[_PyT] (ABC)
- βββ FixLenField[_PyT]
- βββ VarLenField[_PyT]
- β βββ C_n (str β bytes)
- β βββ B_n (bytes)
- βββ KxLenField
- βββ KxU_1
- βββ KxU_2
- βββ KxC_n
- βββ KxR_4
+```bash
+python -m pip install pystdf4
```
-These classes standardize how data flows between **Python**, **C-style internal representations**, and **STDF binary data**.
+### Writing an STDF v4 file
----
+```python
+import time
+from pystdf4 import Stdf4Writer
-## π Data Flow
+with Stdf4Writer('example.stdf') as stdf:
+ # File Attributes Record
+ stdf.FAR(CPU_TYPE=2, STDF_VER=4)
-The transformation pipeline for STDF data in **pystdf4** can be summarized as follows:
+ # Master Information Record
+ stdf.MIR(
+ SETUP_T=int(time.time()),
+ START_T=int(time.time()),
+ STAT_NUM=0,
+ BURN_TIM=0,
+ LOT_ID="Example Lot",
+ JOB_NAM="Example Job",
+ PART_TYP="",
+ NODE_NAM="",
+ TSTR_TYP="",
+ )
+
+ # Add other records here...
-```
-py_value (Python type)
- β [build_py / parse_py]
-internal_bytes (C-style binary data)
- β [build_stdf / parse_stdf]
-stdf_value (STDF field data)
```
-This design ensures each data element can:
+---
-* be **parsed** from STDF binary files,
-* be **converted** into native Python types for analysis or manipulation,
-* and be **re-serialized** back into valid STDF format.
+## π STDF Record Implementation Status
+
+| Record | Type | Sub | Status | Notes |
+| ------ | ---- | --- | ------------- | ---------------------- |
+| FAR | 0 | 10 | βοΈ Complete | Required first record |
+| ATR | 0 | 20 | βοΈ Complete | Audit trail |
+| MIR | 1 | 10 | βοΈ Complete | Lot-level info |
+| MRR | 1 | 20 | βοΈ Complete | End of lot summary |
+| PCR | 1 | 30 | βοΈ Complete | Part statistics |
+| HBR | 1 | 40 | βοΈ Complete | Physical bin counts |
+| SBR | 1 | 50 | βοΈ Complete | Logical bin counts |
+| PMR | 1 | 60 | βοΈ Complete | Pin mapping |
+| PGR | 1 | 62 | βοΈ Complete | Pin grouping |
+| PLR | 1 | 63 | βοΈ Complete | Pin display properties |
+| RDR | 1 | 70 | βοΈ Complete | Retest info |
+| SDR | 1 | 80 | βοΈ Complete | Site configuration |
+| WIR | 2 | 10 | βοΈ Complete | Wafer start marker |
+| WRR | 2 | 20 | βοΈ Complete | Wafer summary |
+| WCR | 2 | 30 | βοΈ Complete | Wafer config |
+| PIR | 5 | 10 | βοΈ Complete | Part start marker |
+| PRR | 5 | 20 | βοΈ Complete | Part results |
+| TSR | 10 | 30 | βοΈ Complete | Test summary |
+| PTR | 15 | 10 | βοΈ Complete | Parametric test |
+| MPR | 15 | 15 | β οΈ Incomplete | Multiple parametric |
+| FTR | 15 | 20 | β οΈ Incomplete | Functional test |
+| BPS | 20 | 10 | βοΈ Complete | Program section start |
+| EPS | 20 | 20 | βοΈ Complete | Program section end |
+| GDR | 50 | 10 | β οΈ Incomplete | User-defined data |
+| DTR | 50 | 30 | βοΈ Complete | Datalog comments |
---
+## π STDF v4 Data Types and Python Implementation
-## π STDF v4 Record Types Implementation Status
-
-| Record Type | Name | REC_TYP | REC_SUB | Status | Notes |
-|-------------|-------------------------------------|---------|---------|---------------|---------------------------------|
-| FAR | File Attributes Record | 0 | 10 | βοΈ Complete | Required as first record |
-| ATR | Audit Trail Record | 0 | 20 | βοΈ Complete | Tracks file modifications |
-| MIR | Master Information Record | 1 | 10 | βοΈ Complete | Lot-level information |
-| MRR | Master Results Record | 1 | 20 | βοΈ Complete | End of lot summary |
-| PCR | Part Count Record | 1 | 30 | βοΈ Complete | Part statistics |
-| HBR | Hardware Bin Record | 1 | 40 | βοΈ Complete | Physical binning counts |
-| SBR | Software Bin Record | 1 | 50 | βοΈ Complete | Logical binning counts |
-| PMR | Pin Map Record | 1 | 60 | βοΈ Complete | Pin/channel mapping |
-| PGR | Pin Group Record | 1 | 62 | β Incomplete | Pin grouping |
-| PLR | Pin List Record | 1 | 63 | β Incomplete | Pin group display properties |
-| RDR | Retest Data Record | 1 | 70 | β Incomplete | Retest information |
-| SDR | Site Description Record | 1 | 80 | βοΈ Complete | Test site configuration |
-| WIR | Wafer Information Record | 2 | 10 | βοΈ Complete | Wafer start marker |
-| WRR | Wafer Results Record | 2 | 20 | βοΈ Complete | Wafer completion summary |
-| WCR | Wafer Configuration Record | 2 | 30 | βοΈ Complete | Wafer dimensions/orientation |
-| PIR | Part Information Record | 5 | 10 | βοΈ Complete | Part start marker |
-| PRR | Part Results Record | 5 | 20 | βοΈ Complete | Part completion results |
-| TSR | Test Synopsis Record | 10 | 30 | βοΈ Complete | Test execution statistics |
-| PTR | Parametric Test Record | 15 | 10 | βοΈ Complete | Single parametric test result |
-| MPR | Multiple-Result Parametric Record | 15 | 15 | β Incomplete | Multiple parametric test results|
-| FTR | Functional Test Record | 15 | 20 | β Incomplete | Functional test results |
-| BPS | Begin Program Section Record | 20 | 10 | βοΈ Complete | Program section start marker |
-| EPS | End Program Section Record | 20 | 20 | βοΈ Complete | Program section end marker |
-| GDR | Generic Data Record | 50 | 10 | β Incomplete | User-defined data |
-| DTR | Datalog Text Record | 50 | 30 | β Incomplete | Datalog comments |
+### STDF Type Codes
----
+STDF defines compact type codes specifying how values are stored and interpreted in records. Below is a concise overview of commonly used types:
+
+| Code | Description | C Type Specifier | Notes |
+| :------- | :-------------------------------------------- | :--------------- | :------------------------------------------- |
+| `C*12` | Fixed-length char (12 bytes) | `char[12]` | Left-justified, pad with spaces |
+| `C*n` | Variable-length char (1-byte length prefix) | `char[]` | Length 0β255 |
+| `C*f` | External-length string | `char[]` | Length defined by another field |
+| `U*1` | 1-byte unsigned integer | `unsigned char` | 0β255 |
+| `U*2` | 2-byte unsigned integer | `unsigned short` | 0β65,535 |
+| `U*4` | 4-byte unsigned integer | `unsigned long` | 0β4,294,967,295 |
+| `I*1` | 1-byte signed integer | `char` | β128 to 127 |
+| `I*2` | 2-byte signed integer | `short` | β32,768 to 32,767 |
+| `I*4` | 4-byte signed integer | `long` | β2,147,483,648 to 2,147,483,647 |
+| `R*4` | 4-byte float (IEEE 754) | `float` | Single precision |
+| `R*8` | 8-byte float (IEEE 754) | `double` | Double precision |
+| `B*6` | Fixed-length binary (6 bytes) | `char[6]` | Raw binary |
+| `V*n` | Variable-type field | β | First byte = type code, up to 255 bytes data |
+| `B*n` | Variable-length binary (1-byte length prefix) | `char[]` | Data starts at second byte |
+| `D*n` | Variable-length bit field | `char[]` | First two bytes = bit count; padding zeros |
+| `N*1` | Nibble array (4-bit units) | `char` | High nibble zeroed if odd count |
+| `kxTYPE` | Array of specified type | `TYPE[]` | Length determined by another field |
+_For full STDF v4 type reference, see `pystdf4/doc/stdf-spec.pdf`._
-## π§ͺ Example Use Cases (Coming Soon)
+### Python Implementation
-* Parse STDF records into structured Python objects
-* Convert STDF to human-readable CSV or JSON
-* Build new STDF records programmatically
-* Validate field encoding/decoding across STDF revisions
+PyStdf4 uses a hierarchy of classes to represent STDF fields and records, with each class implementing a specific type of field or record. The hierarchy is as follows:
+
+```
+
+FieldBase(pyT) (ABC)
+βββ ImmediateField(pyT)
+β βββ C_1, C_12, B_1, B_6
+β βββ C_n, B_n, C_f
+β βββ D_n, N_1 [β οΈ Not Implemented Yet]
+β
+βββ DeferredField(pyT)
+β βββ U_1, U_2, U_4
+β βββ I_1, I_2, I_4
+β βββ R_4, R_8
+β
+βββ VariableField
+β βββ V_n [β οΈ Not Implemented Yet]
+β
+βββ ArrayField
+ βββ kxU_1, kxU_2, kxC_n
+ βββ kxN_1 [β οΈ Not Implemented Yet]
+
+```
+
+These classes handle type conversion, byte parsing, and STDF serialization, ensuring consistency between Python objects and STDF binary data.
---
-## π License
+## πΊοΈ Roadmap
-MIT License Β© 2025
-Developed for efficient and reliable STDF data manipulation in modern semiconductor workflows.
+| Version | Goals |
+| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| **v0.x** | β
Core STDF types & common records
β¬ Fully functional `StdfWriter`
β¬ Robust Pythonic writing API |
+| **v1.x** | β¬ SmartWriter (auto record creation)
β¬ High-level APIs for stats & aggregation
β¬ Intelligent record dependency handling |
+| **v2.x** | β¬ `StdfReader` (efficient parsing)
β¬ Integration with analysis pipelines
β¬ Full read/write compatibility |
---
-Would you like me to extend it with an **βInstallation and Quick Startβ** section (e.g., `pip install pystdf4` and a short usage example)? That would make it more complete for public release on GitHub or PyPI.
+## π License
+
+MIT Β© 2025 β Developed for efficient and reliable STDF data manipulation in modern semiconductor workflows.
diff --git a/examples/write_stdf_file.py b/examples/write_stdf_file.py
new file mode 100644
index 0000000..e69de29
diff --git a/main.py b/main.py
deleted file mode 100644
index c6baabe..0000000
--- a/main.py
+++ /dev/null
@@ -1,567 +0,0 @@
-import csv
-import math
-import re
-import struct
-import sys
-from itertools import zip_longest
-from pathlib import Path
-from typing import List, Optional, Tuple
-
-from pystdf4.IO import Stfd4Writer
-
-
-def parse_test_data_segment(test_data_segment: List[str]) -> Tuple[float, ...]:
- return tuple(map(safg_float, test_data_segment))
-
-
-def parse_coordinates_segment(coordinates_segment: List[str]) -> Tuple[int, ...]:
- return tuple(map(int, coordinates_segment))
-
-
-def parse_moving_limit_segment(moving_limit_segment: str) -> Tuple[str, ...]:
- return tuple(moving_limit_segment.split(";"))
-
-
-def parse_bins_segment(bins_segment: List[str]) -> Tuple[int, ...]:
- return tuple(map(int, bins_segment))
-
-
-def parse_site_info_segment(
- site_info_segment: List[str],
-) -> Tuple[Tuple[int, ...], ...]:
- site_number = len(site_info_segment) // 3
- if site_number * 3 != len(site_info_segment):
- raise ValueError("Site info segment should have 3 values per site.")
- return tuple(
- tuple(
- map(
- safe_int,
- [
- site_info_segment[i],
- site_info_segment[site_number + 2 * i],
- site_info_segment[site_number + 2 * i + 1],
- ],
- )
- )
- for i in range(site_number)
- )
-
-
-def safg_float(value: str) -> float:
- """
- Convert a string to float, and return nan if the string is empty.
-
- Args:
- value (str): _description_
-
- Returns:
- float: _description_
- """
- return float(value) if value else math.nan
-
-
-def safe_int(value: str) -> int:
- """
- Convert a string to int, and return 0 if the string is empty.
-
- Args:
- value (str): _description_
-
- Returns:
- int: _description_
- """
- return int(value) if value else 0
-
-
-class PTR_D:
- __slots__ = (
- "test_number",
- "result",
- "test_txt",
- "lo_limit",
- "hi_limit",
- "unit",
- "test_flag",
- )
-
- def __init__(
- self,
- test_number: int,
- result: float,
- test_txt: str,
- lo_limit: float,
- hi_limit: float,
- unit: str,
- test_pass: Optional[bool],
- ):
- """
- Part test parameters
-
- Args:
- test_number (int): _description_
- site_number (int): _description_
- result (float): _description_
- test_txt (str): _description_
- lo_limit (float): _description_
- hi_limit (float): _description_
- unit (str): _description_
- test_flag (bool, optional): _description_. Defaults to None.
- """
- self.test_number = test_number
- self.result = result
- self.test_txt = test_txt
- self.lo_limit = lo_limit
- self.hi_limit = hi_limit
- self.unit = unit
- # test_pass may be True, False or None
- # if test_pass is None, it means test_flg should be calculated based on lo_limit, hi_limit and result
- # if test_pass in True or False, directly set test_flg based on test_pass
- test_pass = test_pass if test_pass is not None else (lo_limit <= result <= hi_limit)
- self.test_flag = 0
- if not test_pass:
- self.test_flag |= 1 << 7
-
-
-class PRR_D:
- __slots__ = ("hard_bin", "soft_bin", "x_coord", "y_coord", "test_t", "part_id")
-
- @property
- def part_flag(self) -> int:
- return 0 if self.hard_bin == 1 else 8
-
- def __init__(
- self,
- hard_bin: int,
- soft_bin: int,
- x_coord: int,
- y_coord: int,
- test_t: int,
- part_id: int,
- ):
- self.hard_bin = hard_bin
- self.soft_bin = soft_bin
- self.x_coord = x_coord
- self.y_coord = y_coord
- self.test_t = test_t
- self.part_id = part_id
-
-
-class Part:
- __slots__ = ("site_num", "PTRs", "PRR")
- PRR: Optional[PRR_D]
- PTRs: Tuple[PTR_D, ...]
-
- def __init__(self, site_num: int = -1):
- self.site_num = site_num
- self.PRR = None
- self.PTRs = tuple()
-
- def update_ptrs(self, ptrs: Tuple[PTR_D, ...]):
- self.PTRs = ptrs
-
- def edit_prr(
- self,
- hard_bin: int,
- soft_bin: int,
- x_coord: int,
- y_coord: int,
- test_t: int,
- part_id: int,
- ):
- self.PRR = PRR_D(
- hard_bin=hard_bin,
- soft_bin=soft_bin,
- x_coord=x_coord,
- y_coord=y_coord,
- test_t=test_t,
- part_id=part_id,
- )
-
- def record(self):
- pass
-
-
-class StdfGenerator:
- csv_site_header_partten = re.compile(r"^Site(\d+) (Time|SOT|EOT)")
- pat_item_pattern = re.compile(r"\[\[?(\w+)&\w+(\d+)\|([\d\.]+)\]?\]")
-
- @property
- def site_count(self) -> int:
- return len(self.site_indices)
-
- @property
- def test_count(self) -> int:
- return len(self.lower_limits)
-
- def __init__(
- self,
- lower_limits: Tuple[float, ...],
- higher_limits: Tuple[float, ...],
- units: Tuple[str, ...],
- test_txts: Tuple[str, ...],
- site_indices: Tuple[int, ...],
- ):
- """
- Generate parts based on the given parameters.
-
- Args:
- lower_limits (Tuple[float, ...]): _description_
- higher_limits (Tuple[float, ...]): _description_
- units (Tuple[str, ...]): _description_
- test_txts (Tuple[str, ...]): _description_
- site_indices (Tuple[int, ...]): _description_
- """
- # Check input parameters validity
- if len(lower_limits) != len(higher_limits) or len(lower_limits) != len(units):
- raise ValueError("Length of limits, units and test_txts should be equal.")
-
- # Check site indices validity
- if len(site_indices) == 0:
- raise ValueError("Site indices should not be empty.")
-
- self.lower_limits = lower_limits
- self.higher_limits = higher_limits
- self.units = units
- self.test_txts = test_txts
- self.site_indices = site_indices
-
- # data properties
- self.parts: List[Part] = []
-
- # Stastics properties
- self.failed_count: dict[str, int] = {txt: 0 for txt in test_txts}
- self.failed_dut_count: int = 0
- self.passed_dut_count: int = 0
-
- def __repr__(self) -> str:
- repr_str = f"StdfGenerator(Site indices: {self.site_indices}; Test Items: {self.test_count})"
- for i, (lower_limit, higher_limit, unit, test_txt) in enumerate(
- zip(self.lower_limits, self.higher_limits, self.units, self.test_txts)
- ):
- repr_str += f"\n\tIndex: {i + 1}; Test Text: {test_txt}; Limit: [{lower_limit} - {higher_limit} {unit}]"
-
- return repr_str
-
- @classmethod
- def generate_stdf_object(
- cls,
- lower_limit_header: List[str],
- higher_limit_header: List[str],
- unit_header: List[str],
- test_txt_header: List[str],
- ) -> "StdfGenerator":
- lower_limits, higher_limits, units, test_txts = [], [], [], []
- site_indices = set()
-
- for lower_limit, higher_limit, unit, test_txt in zip_longest(
- lower_limit_header,
- higher_limit_header,
- unit_header,
- test_txt_header,
- fillvalue="",
- ):
- if test_txt in {"X", "Y", "MovingLimitFail", "HardBin", "SoftBin"}:
- continue
- elif match_rst := cls.csv_site_header_partten.match(test_txt):
- site_index = int(match_rst.group(1))
- site_indices.add(site_index)
- else:
- lower_limits.append(float(lower_limit))
- higher_limits.append(float(higher_limit))
- units.append(unit)
- test_txts.append(test_txt)
- return cls(
- tuple(lower_limits),
- tuple(higher_limits),
- tuple(units),
- tuple(test_txts),
- tuple(site_indices),
- )
-
- def evaluate_part_test(
- self,
- test_results: Tuple[float, ...],
- coordinates: Tuple[int, ...],
- moving_limit: Tuple[str, ...],
- bins: Tuple[int, ...],
- sites_information: Tuple[Tuple[int, ...], ...],
- part_id: int,
- ):
- """
- Evaluate the test result and return the test flag.
-
- Args:
- test_results (Tuple[float, ...]): _description_
- """
- # Check input parameters validity
- if len(test_results) != self.test_count:
- raise ValueError("Length of test_results should be equal to test_count.")
-
- # Get actual site number according to the SOT/EOT and Time
- site_number = -1
- for site_index, site_info in enumerate(sites_information):
- if sum(site_info) == 0:
- continue
- elif site_number == -1:
- site_number = site_index
- else:
- raise ValueError("Site number should be set only once!")
-
- # Create Part object and update PTRs
- if site_number == -1:
- raise ValueError("Site number should be set!")
-
- part_obj = Part(self.site_indices[site_number])
-
- ptrs = []
- moving_limit_set = set(moving_limit)
-
- # Append PTR objects to ptrs list
- for i, (lower_limit, higher_limit, test_txt, unit, result) in enumerate(
- zip(
- self.lower_limits,
- self.higher_limits,
- self.test_txts,
- self.units,
- test_results,
- ),
- start=1,
- ):
- test_pass = test_txt not in moving_limit_set if self.pat_item_pattern.match(test_txt) else None
- ptr = PTR_D(
- test_number=i,
- result=result,
- test_txt=test_txt,
- lo_limit=lower_limit,
- hi_limit=higher_limit,
- unit=unit,
- test_pass=test_pass,
- )
- if ptr.test_flag != 0:
- self.failed_count[test_txt] += 1
-
- ptrs.append(ptr)
-
- # Update PRR_D object
- part_obj.update_ptrs(tuple(ptrs))
-
- # Edit PRR_D object
- part_obj.edit_prr(
- hard_bin=bins[0],
- soft_bin=bins[1],
- x_coord=coordinates[0],
- y_coord=coordinates[1],
- test_t=sites_information[site_number][0],
- part_id=part_id,
- )
- if bins[0] != 1:
- self.failed_dut_count += 1
- else:
- self.passed_dut_count += 1
-
- self.parts.append(part_obj)
-
- @staticmethod
- def _gen_stdf_record(typ: int, sub: int, data: bytes) -> bytes:
- return struct.pack("=4.0.1",
- "cython>=3.2.1",
"pytest>=8.3.5",
- "setuptools>=75.3.2",
]
diff --git a/pystdf4/Core/__init__.py b/pystdf4/Core/__init__.py
index 9436977..b9bb9f9 100644
--- a/pystdf4/Core/__init__.py
+++ b/pystdf4/Core/__init__.py
@@ -1,31 +1,22 @@
-from .data_type import (
- B_1,
- C_1,
- I_1,
- I_2,
- I_4,
- R_4,
- R_8,
- U_1,
- U_2,
- U_4,
- B_n,
- C_n,
-)
+from .data_type import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n, kxC_n, kxR_4, kxU_1, kxU_2
from .dynamic_buffer import DynamicBuffer
__all__ = [
"DynamicBuffer",
- "U_1",
- "U_2",
- "U_4",
+ "B_1",
+ "C_1",
"I_1",
"I_2",
"I_4",
"R_4",
"R_8",
- "C_1",
- "C_n",
- "B_1",
+ "U_1",
+ "U_2",
+ "U_4",
"B_n",
+ "C_n",
+ "kxC_n",
+ "kxR_4",
+ "kxU_1",
+ "kxU_2",
]
diff --git a/pystdf4/Core/data_base.py b/pystdf4/Core/data_base.py
index 65d9e4c..793fbbe 100644
--- a/pystdf4/Core/data_base.py
+++ b/pystdf4/Core/data_base.py
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
from struct import Struct
-from typing import Any, ClassVar, Generic, Sequence, TypeVar
+from typing import Any, ClassVar, Generic, Optional, Sequence, Type, TypeVar
from pystdf4.Core.dynamic_buffer import DynamicBuffer
from pystdf4.Core.mixins import CacheMixin
@@ -19,7 +19,7 @@
class FieldBase(ABC, Generic[pyT]):
@classmethod
@abstractmethod
- def pack_into(cls, buffer: DynamicBuffer, value: pyT):
+ def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0):
raise NotImplementedError()
@classmethod
@@ -42,8 +42,8 @@ class ImmediateField(FieldBase[pyT]):
field_size: ClassVar[int] = 0
@classmethod
- def pack_into(cls, buffer: DynamicBuffer, value: pyT):
- data = cls._normalize_value(value)
+ def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0):
+ data = cls._normalize_value(value, size)
field_size = cls.field_size if cls.field_size else len(data)
buffer._ensure_capacity(field_size)
buffer._mv[buffer.offset : buffer.offset + field_size] = data
@@ -54,9 +54,13 @@ def pack_into(cls, buffer: DynamicBuffer, value: pyT):
def _pascal_bytes(value: bytes) -> bytes:
return bytes((len(value),)) + value
+ @staticmethod
+ def _left_justified_bytes(value: bytes, size: int, fill_byte: bytes) -> bytes:
+ return value.ljust(size, fill_byte)
+
@staticmethod
@abstractmethod
- def _normalize_value(value: Any) -> bytes:
+ def _normalize_value(value: Any, size: int = 0) -> bytes:
raise NotImplementedError()
@@ -72,7 +76,7 @@ def __init_subclass__(cls) -> None:
cls.field_size = Struct(f"{cls.endian}{cls.num_elements}{cls.struct_format}").size
@classmethod
- def pack_into(cls, buffer: DynamicBuffer, value: pyT):
+ def pack_into(cls, buffer: DynamicBuffer, value: pyT, size: int = 0):
"""Reserve space, cache the value, and advance the buffer offset."""
buffer._ensure_capacity(cls.field_size)
cls._enqueue_value(value, buffer.offset, cls.field_size)
@@ -89,3 +93,17 @@ def _serialize_sequence(cls, sequence: Sequence[pyT]) -> bytes:
count = len(cls.buffer_offsets) * cls.num_elements
packer = Struct(f"{cls.endian}{count}{cls.struct_format}")
return packer.pack(*sequence)
+
+
+class ArrayField:
+ element_type: ClassVar[Type[FieldBase]]
+
+ @classmethod
+ def pack_into(cls, buffer: DynamicBuffer, value: Optional[Sequence[pyT]], size: int):
+ if value is None:
+ return
+ if len(value) > size:
+ value = value[:size]
+
+ for element in value:
+ cls.element_type.pack_into(buffer, element)
diff --git a/pystdf4/Core/data_type.py b/pystdf4/Core/data_type.py
index 655659b..43786de 100644
--- a/pystdf4/Core/data_type.py
+++ b/pystdf4/Core/data_type.py
@@ -1,4 +1,4 @@
-from pystdf4.Core.data_base import DeferredField, ImmediateField
+from pystdf4.Core.data_base import ArrayField, DeferredField, ImmediateField
# ============================================================
# Numeric Field Implementations
@@ -42,29 +42,77 @@ class R_8(DeferredField[float]):
# ============================================================
-class C_1(ImmediateField):
+class C_1(ImmediateField[str]):
field_size = 1
@staticmethod
- def _normalize_value(value: str) -> bytes:
+ def _normalize_value(value: str, size: int = 0) -> bytes:
return value.encode("ascii")
-class B_1(ImmediateField):
+class C_12(ImmediateField[str]):
+ field_size = 12
+
+ @staticmethod
+ def _normalize_value(value: str, size: int = 0) -> bytes:
+ return value.encode("ascii")
+
+
+class B_1(ImmediateField[bytes]):
field_size = 1
@staticmethod
- def _normalize_value(value: bytes) -> bytes:
+ def _normalize_value(value: bytes, size: int = 0) -> bytes:
+ return value
+
+
+class B_6(ImmediateField[bytes]):
+ field_size = 6
+
+ @staticmethod
+ def _normalize_value(value: bytes, size: int = 0) -> bytes:
return value
-class C_n(ImmediateField):
+class C_n(ImmediateField[str]):
@staticmethod
- def _normalize_value(value: str) -> bytes:
+ def _normalize_value(value: str, size: int = 0) -> bytes:
return ImmediateField._pascal_bytes(value.encode("ascii"))
-class B_n(ImmediateField):
+class B_n(ImmediateField[bytes]):
@staticmethod
- def _normalize_value(value: bytes) -> bytes:
+ def _normalize_value(value: bytes, size: int = 0) -> bytes:
return ImmediateField._pascal_bytes(value)
+
+
+class C_f(ImmediateField[str]):
+ @staticmethod
+ def _normalize_value(value: bytes, size: int = 0) -> bytes:
+ return ImmediateField._left_justified_bytes(value, size, b" ")
+
+
+# ============================================================
+# Array Field Implementations
+# ============================================================
+
+
+class kxU_1(ArrayField):
+ element_type = U_1
+
+
+class kxU_2(ArrayField):
+ element_type = U_2
+
+
+class kxC_n(ArrayField):
+ element_type = C_n
+
+
+class kxR_4(ArrayField):
+ element_type = R_4
+
+
+# TODO: Implement N_1 field!
+# class kxN_1(ArrayField):
+# element_type = N_1
diff --git a/pystdf4/Core/dynamic_buffer.pyx b/pystdf4/Core/dynamic_buffer.pyx
deleted file mode 100644
index 158f5fe..0000000
--- a/pystdf4/Core/dynamic_buffer.pyx
+++ /dev/null
@@ -1,61 +0,0 @@
-# cython: boundscheck=False, wraparound=False, initializedcheck=False
-# cython: cdivision=True
-# distutils: language = c++
-
-cdef class DynamicBuffer:
- """
- High-performance dynamically resizable byte buffer.
- Cython accelerated version.
- """
-
- cdef public bytearray _buffer
- cdef public unsigned char[:] _mv
- cdef Py_ssize_t _capacity
- cdef public Py_ssize_t offset
-
- def __init__(self, int initial_capacity=1024*1024):
- self._buffer = bytearray(initial_capacity)
- self._mv = self._buffer
- self._capacity = initial_capacity
- self.offset = 0
-
- # ---- properties ----
-
- @property
- def capacity(self) -> int:
- return self._capacity
-
- @capacity.setter
- def capacity(self, int value):
- if value < self.offset:
- raise ValueError(
- f"Cannot shrink below current offset ({self.offset} bytes)"
- )
-
- cdef bytearray new_buf = bytearray(value)
- new_buf[:self.offset] = self._buffer[:self.offset]
-
- self._buffer = new_buf
- self._mv = new_buf
- self._capacity = value
-
- def __len__(self):
- return self.offset
-
- def __repr__(self):
- return f""
-
- # ---- private but Python-callable ----
- cpdef void _ensure_capacity(self, Py_ssize_t size):
- cdef Py_ssize_t target = self.offset + size
- cdef Py_ssize_t desired = self._capacity
-
- if target > desired:
- while desired < target:
- desired = (desired * 3 + 1) >> 1
- self.capacity = desired
-
- # ---- public ----
-
- def to_bytes(self) -> bytes:
- return bytes(self._mv[:self.offset])
diff --git a/pystdf4/IO/__init__.py b/pystdf4/IO/__init__.py
index f04417f..0625049 100644
--- a/pystdf4/IO/__init__.py
+++ b/pystdf4/IO/__init__.py
@@ -1,4 +1,4 @@
from .stdf4reader import Stdf4Reader
-from .stfd4write import Stfd4Writer
+from .stdf4writer import Stdf4Writer
-__all__ = ["Stdf4Reader", "Stfd4Writer"]
+__all__ = ["Stdf4Reader", "Stdf4Writer"]
diff --git a/pystdf4/IO/base.py b/pystdf4/IO/base.py
index d5933bd..e69de29 100644
--- a/pystdf4/IO/base.py
+++ b/pystdf4/IO/base.py
@@ -1,48 +0,0 @@
-from pathlib import Path
-from struct import Struct
-
-from pystdf4.Core import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n
-from pystdf4.Core.dynamic_buffer import DynamicBuffer
-
-
-class StdfIOBase:
- __slots__ = (
- "file_path",
- "buffer",
- "B_1",
- "C_1",
- "I_1",
- "I_2",
- "I_4",
- "R_4",
- "R_8",
- "U_1",
- "U_2",
- "U_4",
- "B_n",
- "C_n",
- "KxC_n",
- "KxR_4",
- "KxU_1",
- "KxU_2",
- "header_packer",
- )
-
- def __init__(self, file_path: str):
- self.buffer = DynamicBuffer()
- self.file_path = Path(file_path)
-
- self.B_1 = B_1()
- self.C_1 = C_1()
- self.I_1 = I_1()
- self.I_2 = I_2()
- self.I_4 = I_4()
- self.R_4 = R_4()
- self.R_8 = R_8()
- self.U_1 = U_1()
- self.U_2 = U_2()
- self.U_4 = U_4()
- self.B_n = B_n()
- self.C_n = C_n()
-
- self.header_packer: Struct = Struct(" "Stdf4Writer":
+ if not self.file_path.exists():
+ self.file_path.touch()
+ return self
+
+ def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: object) -> None:
+ self._write_buffers()
+ self.file_path.write_bytes(self.buffer.to_bytes())
+
+ # endregion
+
+ def _write_buffers(self):
+ for scalar_field in (
+ self.U_1,
+ self.U_2,
+ self.U_4,
+ self.I_1,
+ self.I_2,
+ self.I_4,
+ self.R_4,
+ self.R_8,
+ ):
+ scalar_field.flush_cache_to_buffer(self.buffer)
diff --git a/pystdf4/IO/stfd4write.py b/pystdf4/IO/stfd4write.py
deleted file mode 100644
index 4eef6aa..0000000
--- a/pystdf4/IO/stfd4write.py
+++ /dev/null
@@ -1,619 +0,0 @@
-from typing import Any, Callable, Literal, Optional, Sequence, Tuple
-
-from .base import StdfIOBase
-
-
-class Stfd4Writer(StdfIOBase):
- def __init__(self, file_path: str):
- super().__init__(file_path)
-
- def to_bytes(self) -> bytes:
- self.write_buffers()
- return self.buffer.to_bytes()
-
- def write_fields(self, rec_typ: int, rec_sub: int, field_writer: Callable):
- # Step 1: Write the header of the record
- record_start = self._write_header((rec_typ, rec_sub))
-
- # Step 2: Write the fields of the record
- field_writer(self)
-
- # Step 3: Update the length of the record
- record_end = self.buffer.offset
- record_length = record_end - (record_start + 4)
- self.buffer._mv[record_start : record_start + 2] = record_length.to_bytes(2, byteorder="little")
-
- def _write_header(self, header: Tuple[int, int]) -> int:
- """
- Write the header of a record to the file.
- """
- packer_size = self.header_packer.size
- self.buffer._ensure_capacity(packer_size)
- start = self.buffer.offset
- self.header_packer.pack_into(self.buffer._mv, start, 0, *header)
- self.buffer.offset += packer_size
- return start
-
- def write_buffers(self):
- for scalar_field in (self.U_1, self.U_2, self.U_4, self.I_1, self.I_2, self.I_4, self.R_4, self.R_8):
- scalar_field.flush_cache_to_buffer(self.buffer)
-
- def ATR(self, MOD_TIM: int, CMD_LINE: str):
- """Audit Trail Record"""
-
- # Implementation here
- def write_fields(self):
- self.U_4.pack_into(self.buffer, MOD_TIM)
- self.C_n.pack_into(self.buffer, CMD_LINE)
-
- self.write_fields(0, 20, write_fields)
-
- def BPS(self, SEQ_NAME: str = ""):
- """Begin Program Section Record"""
- # Implementation here
- pass
-
- def DTR(self, TEXT_DAT: str):
- """Datalog Text Record"""
- # Implementation here
- pass
-
- def EPS(self):
- """End Program Section Record"""
- # Implementation here
- pass
-
- def FAR(self, CPU_TYPE: int, STDF_VER: int):
- """File Attributes Record"""
-
- # Implementation here
- def write_fields(self):
- self.U_1.pack_into(self.buffer, CPU_TYPE)
- self.U_1.pack_into(self.buffer, STDF_VER)
-
- self.write_fields(0, 10, write_fields)
-
- def FTR(
- self,
- TEST_NUM: int,
- HEAD_NUM: int,
- SITE_NUM: int,
- TEST_FLG: bytes,
- OPT_FLAG: bytes,
- RTN_ICNT: int,
- PGM_ICNT: int,
- CYCL_CNT: int = 0,
- REL_VADR: int = 0,
- REPT_CNT: int = 0,
- NUM_FAIL: int = 0,
- XFAIL_AD: int = 0,
- YFAIL_AD: int = 0,
- VECT_OFF: int = 0,
- RTN_INDX: Optional[Sequence[int]] = None,
- RTN_STAT: Optional[Sequence[bytes]] = None,
- PGM_INDX: Optional[Sequence[int]] = None,
- PGM_STAT: Optional[Sequence[bytes]] = None,
- FAIL_PIN: bytes = b"",
- VECT_NAM: str = "",
- TIME_SET: str = "",
- OP_CODE: str = "",
- TEST_TXT: str = "",
- ALARM_ID: str = "",
- PROG_TXT: str = "",
- RSLT_TXT: str = "",
- PATG_NUM: int = 255,
- SPIN_MAP: bytes = b"",
- ):
- """Functional Test Record"""
- # Implementation here
- pass
-
- def GDR(self, FLD_CNT: int, GEN_DATA: Any):
- """Generic Data Record"""
- # Implementation here
- pass
-
- def HBR(
- self,
- SITE_NUM: int,
- HBIN_NUM: int,
- HBIN_CNT: int,
- HEAD_NUM: int = 255,
- HBIN_PF: Literal["P", "F", " "] = " ",
- HBIN_NAM: str = "",
- ):
- """Hardware Bin Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
- self.U_2.pack_into(self.buffer, HBIN_NUM)
- self.U_4.pack_into(self.buffer, HBIN_CNT)
- self.C_1.pack_into(self.buffer, HBIN_PF)
- self.C_n.pack_into(self.buffer, HBIN_NAM)
-
- self.write_fields(1, 40, write_fields)
-
- def MIR(
- self,
- SETUP_T: int,
- START_T: int,
- STAT_NUM: int,
- LOT_ID: str,
- PART_TYP: str,
- NODE_NAM: str,
- TSTR_TYP: str,
- JOB_NAM: str,
- MODE_COD: Literal[
- "A",
- "C",
- "D",
- "E",
- "M",
- "P",
- "Q",
- "0",
- "1",
- "2",
- # Implementation here
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- " ",
- ] = " ",
- RTST_COD: Literal["Y", "N", "1", "2", "3", "4", "5", "6", "7", "8", "9", " "] = " ",
- PROT_COD: str = " ",
- BURN_TIM: int = 65535,
- CMOD_COD: str = " ",
- JOB_REV: str = "",
- SBLOT_ID: str = "",
- OPER_NAM: str = "",
- EXEC_TYP: str = "",
- EXEC_VER: str = "",
- TEST_COD: str = "",
- TST_TEMP: str = "",
- USER_TXT: str = "",
- AUX_FILE: str = "",
- PKG_TYP: str = "",
- FAMLY_ID: str = "",
- DATE_COD: str = "",
- FACIL_ID: str = "",
- FLOOR_ID: str = "",
- PROC_ID: str = "",
- OPER_FRQ: str = "",
- SPEC_NAM: str = "",
- SPEC_VER: str = "",
- FLOW_ID: str = "",
- SETUP_ID: str = "",
- DSGN_REV: str = "",
- ENG_ID: str = "",
- ROM_COD: str = "",
- SERL_NUM: str = "",
- SUPR_NAM: str = "",
- ):
- """Master Information Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_4.pack_into(self.buffer, SETUP_T)
- self.U_4.pack_into(self.buffer, START_T)
- self.U_1.pack_into(self.buffer, STAT_NUM)
- self.C_1.pack_into(self.buffer, MODE_COD)
- self.C_1.pack_into(self.buffer, RTST_COD)
- self.C_1.pack_into(self.buffer, PROT_COD)
- self.U_2.pack_into(self.buffer, BURN_TIM)
- self.C_1.pack_into(self.buffer, CMOD_COD)
- self.C_n.pack_into(self.buffer, LOT_ID)
- self.C_n.pack_into(self.buffer, PART_TYP)
- self.C_n.pack_into(self.buffer, NODE_NAM)
- self.C_n.pack_into(self.buffer, TSTR_TYP)
- self.C_n.pack_into(self.buffer, JOB_NAM)
- self.C_n.pack_into(self.buffer, JOB_REV)
- self.C_n.pack_into(self.buffer, SBLOT_ID)
- self.C_n.pack_into(self.buffer, OPER_NAM)
- self.C_n.pack_into(self.buffer, EXEC_TYP)
- self.C_n.pack_into(self.buffer, EXEC_VER)
- self.C_n.pack_into(self.buffer, TEST_COD)
- self.C_n.pack_into(self.buffer, TST_TEMP)
- self.C_n.pack_into(self.buffer, USER_TXT)
- self.C_n.pack_into(self.buffer, AUX_FILE)
- self.C_n.pack_into(self.buffer, PKG_TYP)
- self.C_n.pack_into(self.buffer, FAMLY_ID)
- self.C_n.pack_into(self.buffer, DATE_COD)
- self.C_n.pack_into(self.buffer, FACIL_ID)
- self.C_n.pack_into(self.buffer, FLOOR_ID)
- self.C_n.pack_into(self.buffer, PROC_ID)
- self.C_n.pack_into(self.buffer, OPER_FRQ)
- self.C_n.pack_into(self.buffer, SPEC_NAM)
- self.C_n.pack_into(self.buffer, SPEC_VER)
- self.C_n.pack_into(self.buffer, FLOW_ID)
- self.C_n.pack_into(self.buffer, SETUP_ID)
- self.C_n.pack_into(self.buffer, DSGN_REV)
- self.C_n.pack_into(self.buffer, ENG_ID)
- self.C_n.pack_into(self.buffer, ROM_COD)
- self.C_n.pack_into(self.buffer, SERL_NUM)
- self.C_n.pack_into(self.buffer, SUPR_NAM)
-
- self.write_fields(1, 10, write_fields)
-
- def MPR(
- self,
- TEST_NUM: int,
- HEAD_NUM: int,
- SITE_NUM: int,
- TEST_FLG: bytes,
- PARM_FLG: bytes,
- RTN_ICNT: int,
- RSLT_CNT: int,
- OPT_FLAG: bytes,
- RTN_STAT: Optional[Sequence[bytes]] = None,
- RTN_RSLT: Optional[Sequence[float]] = None,
- TEST_TXT: str = "",
- ALARM_ID: str = "",
- RES_SCAL: int = 0,
- LLM_SCAL: int = 0,
- HLM_SCAL: int = 0,
- LO_LIMIT: float = 0.0,
- HI_LIMIT: float = 0.0,
- START_IN: float = 0.0,
- INCR_IN: float = 0.0,
- RTN_INDX: Optional[Sequence[int]] = None,
- UNITS: str = "",
- UNITS_IN: str = "",
- C_RESFMT: str = "",
- C_LLMFMT: str = "",
- C_HLMFMT: str = "",
- LO_SPEC: float = 0.0,
- HI_SPEC: float = 0.0,
- ):
- """Multiple-Result Parametric Record"""
- # Implementation here
-
- def MRR(
- self,
- FINISH_T: int,
- DISP_COD: str = " ",
- USR_DESC: str = "",
- EXC_DESC: str = "",
- ):
- """Master Results Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_4.pack_into(self.buffer, FINISH_T)
- self.C_1.pack_into(self.buffer, DISP_COD)
- self.C_n.pack_into(self.buffer, USR_DESC)
- self.C_n.pack_into(self.buffer, EXC_DESC)
-
- self.write_fields(1, 20, write_fields)
-
- def PCR(
- self,
- SITE_NUM: int,
- PART_CNT: int,
- HEAD_NUM: int = 255,
- RTST_CNT: int = 4294967295,
- ABRT_CNT: int = 4294967295,
- GOOD_CNT: int = 4294967295,
- FUNC_CNT: int = 4294967295,
- ):
- """Part Count Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
- self.U_4.pack_into(self.buffer, PART_CNT)
- self.U_4.pack_into(self.buffer, RTST_CNT)
- self.U_4.pack_into(self.buffer, ABRT_CNT)
- self.U_4.pack_into(self.buffer, GOOD_CNT)
- self.U_4.pack_into(self.buffer, FUNC_CNT)
-
- self.write_fields(1, 30, write_fields)
-
- def PGR(
- self,
- GRP_INDX: int,
- INDX_CNT: int,
- PMR_INDX: Optional[Sequence[int]] = None,
- GRP_NAM: str = "",
- ):
- """Pin Group Record"""
- # Implementation here
- pass
-
- def PIR(
- self,
- HEAD_NUM: int,
- SITE_NUM: int,
- ):
- """Part Information Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
-
- self.write_fields(5, 10, write_fields)
-
- def PLR(
- self,
- GRP_CNT: int,
- GRP_INDX: Optional[Sequence[int]] = None,
- GRP_MODE: Optional[Sequence[int]] = None,
- GRP_RADX: Optional[Sequence[int]] = None,
- PGM_CHAR: Optional[Sequence[str]] = None,
- RTN_CHAR: Optional[Sequence[str]] = None,
- PGM_CHAL: Optional[Sequence[str]] = None,
- RTN_CHAL: Optional[Sequence[str]] = None,
- ):
- """Pin List Record"""
- # Implementation here
- pass
-
- def PMR(
- self,
- PMR_INDX: int,
- CHAN_TYP: int = 0,
- CHAN_NAM: str = "",
- PHY_NAM: str = "",
- LOG_NAM: str = "",
- HEAD_NUM: int = 1,
- SITE_NUM: int = 1,
- ):
- """Pin Map Record"""
- # Implementation here
- pass
-
- def PRR(
- self,
- HEAD_NUM: int,
- SITE_NUM: int,
- PART_FLG: bytes,
- NUM_TEST: int,
- HARD_BIN: int,
- SOFT_BIN: int = 65535,
- X_COORD: int = -32768,
- Y_COORD: int = -32768,
- TEST_T: int = 0,
- PART_ID: str = "",
- PART_TXT: str = "",
- PART_FIX: bytes = b"",
- ):
- """Part Results Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
- self.B_1.pack_into(self.buffer, PART_FLG)
- self.U_2.pack_into(self.buffer, NUM_TEST)
- self.U_2.pack_into(self.buffer, HARD_BIN)
- self.U_2.pack_into(self.buffer, SOFT_BIN)
- self.I_2.pack_into(self.buffer, X_COORD)
- self.I_2.pack_into(self.buffer, Y_COORD)
- self.U_4.pack_into(self.buffer, TEST_T)
- self.C_n.pack_into(self.buffer, PART_ID)
- self.C_n.pack_into(self.buffer, PART_TXT)
- self.B_n.pack_into(self.buffer, PART_FIX)
-
- self.write_fields(5, 20, write_fields)
-
- def PTR(
- self,
- TEST_NUM: int,
- HEAD_NUM: int,
- SITE_NUM: int,
- TEST_FLG: bytes,
- PARM_FLG: bytes,
- OPT_FLAG: bytes,
- RESULT: float = 0.0,
- TEST_TXT: str = "",
- ALARM_ID: str = "",
- RES_SCAL: int = 0,
- LLM_SCAL: int = 0,
- HLM_SCAL: int = 0,
- LO_LIMIT: float = 0.0,
- HI_LIMIT: float = 0.0,
- UNITS: str = "",
- C_RESFMT: str = "",
- C_LLMFMT: str = "",
- C_HLMFMT: str = "",
- LO_SPEC: float = 0.0,
- HI_SPEC: float = 0.0,
- ):
- """Parametric Test Record"""
-
- def write_fields(self):
- self.U_4.pack_into(self.buffer, TEST_NUM)
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
- self.B_1.pack_into(self.buffer, TEST_FLG)
- self.B_1.pack_into(self.buffer, PARM_FLG)
- self.R_4.pack_into(self.buffer, RESULT)
- self.C_n.pack_into(self.buffer, TEST_TXT)
- self.C_n.pack_into(self.buffer, ALARM_ID)
- self.B_1.pack_into(self.buffer, OPT_FLAG)
- self.I_1.pack_into(self.buffer, RES_SCAL)
- self.I_1.pack_into(self.buffer, LLM_SCAL)
- self.I_1.pack_into(self.buffer, HLM_SCAL)
- self.R_4.pack_into(self.buffer, LO_LIMIT)
- self.R_4.pack_into(self.buffer, HI_LIMIT)
- self.C_n.pack_into(self.buffer, UNITS)
- self.C_n.pack_into(self.buffer, C_RESFMT)
- self.C_n.pack_into(self.buffer, C_LLMFMT)
- self.C_n.pack_into(self.buffer, C_HLMFMT)
- self.R_4.pack_into(self.buffer, LO_SPEC)
- self.R_4.pack_into(self.buffer, HI_SPEC)
-
- self.write_fields(15, 10, write_fields)
-
- def RDR(self, NUM_BINS: int, RTST_BIN: Optional[Sequence[int]] = None):
- """Retest Data Record"""
- # Implementation here
- pass
-
- def SBR(
- self,
- SITE_NUM: int,
- SBIN_NUM: int,
- SBIN_CNT: int,
- HEAD_NUM: int = 255,
- SBIN_PF: Literal["P", "F", " "] = " ",
- SBIN_NAM: str = "",
- ):
- """Software Bin Record"""
- # Implementation here
- pass
-
- def SDR(
- self,
- HEAD_NUM: int,
- SITE_GRP: int,
- SITE_CNT: int,
- SITE_NUM: Optional[Sequence[int]] = None,
- HAND_TYP: str = "",
- HAND_ID: str = "",
- CARD_TYP: str = "",
- CARD_ID: str = "",
- LOAD_TYP: str = "",
- LOAD_ID: str = "",
- DIB_TYP: str = "",
- DIB_ID: str = "",
- CABL_TYP: str = "",
- CABL_ID: str = "",
- CONT_TYP: str = "",
- CONT_ID: str = "",
- LASR_TYP: str = "",
- LASR_ID: str = "",
- EXTR_TYP: str = "",
- EXTR_ID: str = "",
- ):
- """Site Description Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_GRP)
- self.U_1.pack_into(self.buffer, SITE_CNT)
- # self.KxU_1.pack_into(SITE_CNT, SITE_NUM)
- self.C_n.pack_into(self.buffer, HAND_TYP)
- self.C_n.pack_into(self.buffer, HAND_ID)
- self.C_n.pack_into(self.buffer, CARD_TYP)
- self.C_n.pack_into(self.buffer, CARD_ID)
- self.C_n.pack_into(self.buffer, LOAD_TYP)
- self.C_n.pack_into(self.buffer, LOAD_ID)
- self.C_n.pack_into(self.buffer, DIB_TYP)
- self.C_n.pack_into(self.buffer, DIB_ID)
- self.C_n.pack_into(self.buffer, CABL_TYP)
- self.C_n.pack_into(self.buffer, CABL_ID)
- self.C_n.pack_into(self.buffer, CONT_TYP)
- self.C_n.pack_into(self.buffer, CONT_ID)
- self.C_n.pack_into(self.buffer, LASR_TYP)
- self.C_n.pack_into(self.buffer, LASR_ID)
- self.C_n.pack_into(self.buffer, EXTR_TYP)
- self.C_n.pack_into(self.buffer, EXTR_ID)
-
- self.write_fields(1, 80, write_fields)
-
- def TSR(
- self,
- HEAD_NUM: int,
- SITE_NUM: int,
- TEST_NUM: int,
- OPT_FLAG: bytes,
- TEST_TYP: Literal["P", "F", "M", " "] = " ",
- EXEC_CNT: int = 4294967295,
- FAIL_CNT: int = 4294967295,
- ALRM_CNT: int = 4294967295,
- TEST_NAM: str = "",
- SEQ_NAME: str = "",
- TEST_LBL: str = "",
- TEST_TIM: float = 0.0,
- TEST_MIN: float = 0.0,
- TEST_MAX: float = 0.0,
- TST_SUMS: float = 0.0,
- TST_SQRS: float = 0.0,
- ):
- """Test Synopsis Record"""
- # Implementation here
-
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_NUM)
- self.C_1.pack_into(self.buffer, TEST_TYP)
- self.U_4.pack_into(self.buffer, TEST_NUM)
- self.U_4.pack_into(self.buffer, EXEC_CNT)
- self.U_4.pack_into(self.buffer, FAIL_CNT)
- self.U_4.pack_into(self.buffer, ALRM_CNT)
- self.C_n.pack_into(self.buffer, TEST_NAM)
- self.C_n.pack_into(self.buffer, SEQ_NAME)
- self.C_n.pack_into(self.buffer, TEST_LBL)
- self.B_1.pack_into(self.buffer, OPT_FLAG)
- self.R_4.pack_into(self.buffer, TEST_TIM)
- self.R_4.pack_into(self.buffer, TEST_MIN)
- self.R_4.pack_into(self.buffer, TEST_MAX)
- self.R_4.pack_into(self.buffer, TST_SUMS)
- self.R_4.pack_into(self.buffer, TST_SQRS)
-
- self.write_fields(10, 30, write_fields)
-
- def WCR(
- self,
- WAFR_SIZ: float = 0.0,
- DIE_HT: float = 0.0,
- DIE_WID: float = 0.0,
- WF_UNITS: int = 0,
- WF_FLAT: Literal["U", "D", "L", "R", " "] = " ",
- CENTER_X: int = -32768,
- CENTER_Y: int = -32768,
- POS_X: Literal["L", "R", " "] = " ",
- POS_Y: Literal["U", "D", " "] = " ",
- ):
- """Wafer Configuration Record"""
- # Implementation here
- pass
-
- def WIR(
- self,
- HEAD_NUM: int,
- START_T: int,
- SITE_GRP: int = 255,
- WAFER_ID: str = "",
- ):
- """Wafer Information Record"""
-
- # Implementation here
- def write_fields(self):
- self.U_1.pack_into(self.buffer, HEAD_NUM)
- self.U_1.pack_into(self.buffer, SITE_GRP)
- self.U_4.pack_into(self.buffer, START_T)
- self.C_n.pack_into(self.buffer, WAFER_ID)
-
- self.write_fields(2, 10, write_fields)
-
- def WRR(
- self,
- HEAD_NUM: int,
- FINISH_T: int,
- PART_CNT: int,
- SITE_GRP: int = 255,
- RTST_CNT: int = 4294967295,
- ABRT_CNT: int = 4294967295,
- GOOD_CNT: int = 4294967295,
- FUNC_CNT: int = 4294967295,
- WAFER_ID: str = "",
- FABWF_ID: str = "",
- FRAME_ID: str = "",
- MASK_ID: str = "",
- USR_DESC: str = "",
- EXC_DESC: str = "",
- ):
- """Wafer Results Record"""
- # Implementation here
- pass
diff --git a/pystdf4/Records/__init__.py b/pystdf4/Records/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pystdf4/Records/base.py b/pystdf4/Records/base.py
new file mode 100644
index 0000000..d369978
--- /dev/null
+++ b/pystdf4/Records/base.py
@@ -0,0 +1,50 @@
+from struct import Struct
+from typing import Callable, Tuple
+
+from pystdf4.Core import B_1, C_1, I_1, I_2, I_4, R_4, R_8, U_1, U_2, U_4, B_n, C_n, DynamicBuffer, kxC_n, kxR_4, kxU_1, kxU_2
+
+
+class RecordBase:
+ def __init__(self):
+ self.buffer = DynamicBuffer()
+ self.header_packer: Struct = Struct(" int:
+ """
+ Write the header of a record to the file.
+ """
+ packer_size = self.header_packer.size
+ self.buffer._ensure_capacity(packer_size)
+ start = self.buffer.offset
+ self.header_packer.pack_into(self.buffer._mv, start, 0, *header)
+ self.buffer.offset += packer_size
+ return start
diff --git a/pystdf4/Records/literials.py b/pystdf4/Records/literials.py
new file mode 100644
index 0000000..28948ba
--- /dev/null
+++ b/pystdf4/Records/literials.py
@@ -0,0 +1,54 @@
+from typing import Literal
+
+# Basic Literals
+AlphaNumericLiteral = Literal[
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ " ",
+]
+
+# MIR MODE_COD
+StationModeLiteral = Literal["A", "C", "D", "E", "M", "P", "Q", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", " "]
+
+# MIR RTST_COD
+RetestConditionLiteral = Literal["Y", "N", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
+
+# BIN_PF
+BinPassFailLiteral = Literal["P", "F", " "]
+
+# TSR TEST_TYP
+TestTypeLiteral = Literal["P", "F", "M", " "]
diff --git a/pystdf4/Records/packer.py b/pystdf4/Records/packer.py
new file mode 100644
index 0000000..812745b
--- /dev/null
+++ b/pystdf4/Records/packer.py
@@ -0,0 +1,1235 @@
+from typing import Any, Literal, Sequence
+
+from pystdf4.Records.base import RecordBase
+from pystdf4.Records.literials import AlphaNumericLiteral, BinPassFailLiteral, RetestConditionLiteral, StationModeLiteral, TestTypeLiteral
+
+
+class StdfPacker(RecordBase):
+ def __init__(self):
+ super().__init__()
+
+ def FAR(
+ self,
+ CPU_TYPE: int,
+ STDF_VER: int,
+ ) -> None:
+ """File Attributes Record (FAR)
+
+ Contains the information necessary to determine how to decode the STDF data
+ contained in the file.
+
+ Note:
+ Required as the first record of the file.
+
+ Args:
+ CPU_TYPE (int): CPU type that wrote this file.
+ STDF_VER (int): STDF version number.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, CPU_TYPE)
+ obj.U_1.pack_into(obj.buffer, STDF_VER)
+
+ self.write_fields(0, 10, write_fields)
+
+ def ATR(
+ self,
+ MOD_TIM: int,
+ CMD_LINE: str,
+ ) -> None:
+ """Audit Trail Record (ATR)
+
+ Used to record any operation that alters the contents of the STDF file. The name of the
+ program and all its parameters should be recorded in the ASCII field provided in this
+ record. Typically, this record will be used to track filter programs that have been
+ applied to the data.
+
+ Note:
+ Between the File Attributes Record (FAR) and the Master Information Record (MIR).
+ The filter program that writes the altered STDF file must write its ATR immediately
+ after the FAR (and hence before any other ATRs that may be in the file). In this way,
+ multiple ATRs will be in reverse chronological order.
+
+ Args:
+ MOD_TIM (int): Date and time of STDF file modification.
+ CMD_LINE (str): Command line of program.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, MOD_TIM)
+ obj.C_n.pack_into(obj.buffer, CMD_LINE)
+
+ self.write_fields(0, 20, write_fields)
+
+ def MIR(
+ self,
+ SETUP_T: int,
+ START_T: int,
+ STAT_NUM: int,
+ LOT_ID: str,
+ PART_TYP: str,
+ NODE_NAM: str,
+ TSTR_TYP: str,
+ JOB_NAM: str,
+ MODE_COD: StationModeLiteral = " ",
+ RTST_COD: RetestConditionLiteral = " ",
+ PROT_COD: AlphaNumericLiteral = " ",
+ BURN_TIM: int = 65535,
+ CMOD_COD: AlphaNumericLiteral = " ",
+ JOB_REV: str = "",
+ SBLOT_ID: str = "",
+ OPER_NAM: str = "",
+ EXEC_TYP: str = "",
+ EXEC_VER: str = "",
+ TEST_COD: str = "",
+ TST_TEMP: str = "",
+ USER_TXT: str = "",
+ AUX_FILE: str = "",
+ PKG_TYP: str = "",
+ FAMLY_ID: str = "",
+ DATE_COD: str = "",
+ FACIL_ID: str = "",
+ FLOOR_ID: str = "",
+ PROC_ID: str = "",
+ OPER_FRQ: str = "",
+ SPEC_NAM: str = "",
+ SPEC_VER: str = "",
+ FLOW_ID: str = "",
+ SETUP_ID: str = "",
+ DSGN_REV: str = "",
+ ENG_ID: str = "",
+ ROM_COD: str = "",
+ SERL_NUM: str = "",
+ SUPR_NAM: str = "",
+ ):
+ """Master Information Record (MIR)
+
+ The MIR and the MRR (Master Results Record) contain all the global information that
+ is to be stored for a tested lot of parts. Each data stream must have exactly one MIR,
+ immediately after the FAR (and the ATRs, if they are used). This will allow any data
+ reporting or analysis programs access to this information in the shortest possible
+ amount of time.
+
+ Note:
+ Immediately after the File Attributes Record (FAR) and the Audit Trail Records (ATR),
+ if ATRs are used.
+
+ Args:
+ SETUP_T (int): Date and time of job setup.
+ START_T (int): Date and time first part tested.
+ STAT_NUM (int): Tester station number.
+ LOT_ID (str): Lot ID (customer specified).
+ PART_TYP (str): Part Type (or product ID).
+ NODE_NAM (str): Name of node that generated data.
+ TSTR_TYP (str): Tester type.
+ JOB_NAM (str): Job name (test program name).
+ MODE_COD (StationModeLiteral): Test mode code (e.g. prod, dev). Defaults to " ".
+ RTST_COD (RetestConditionLiteral): Lot retest code. Defaults to " ".
+ PROT_COD (AlphaNumericLiteral): Data protection code. Defaults to " ".
+ BURN_TIM (int): Burn-in time (in minutes). Defaults to 65535.
+ CMOD_COD (AlphaNumericLiteral): Command mode code. Defaults to " ".
+ JOB_REV (str): Job (test program) revision number. Defaults to "".
+ SBLOT_ID (str): Sublot ID. Defaults to "".
+ OPER_NAM (str): Operator name or ID (at setup time). Defaults to "".
+ EXEC_TYP (str): Tester executive software type. Defaults to "".
+ EXEC_VER (str): Tester exec software version number. Defaults to "".
+ TEST_COD (str): Test phase or step code. Defaults to "".
+ TST_TEMP (str): Test temperature. Defaults to "".
+ USER_TXT (str): Generic user text. Defaults to "".
+ AUX_FILE (str): Name of auxiliary data file. Defaults to "".
+ PKG_TYP (str): Package type. Defaults to "".
+ FAMLY_ID (str): Product family ID. Defaults to "".
+ DATE_COD (str): Date code. Defaults to "".
+ FACIL_ID (str): Test facility ID. Defaults to "".
+ FLOOR_ID (str): Test floor ID. Defaults to "".
+ PROC_ID (str): Fabrication process ID. Defaults to "".
+ OPER_FRQ (str): Operation frequency or step. Defaults to "".
+ SPEC_NAM (str): Test specification name. Defaults to "".
+ SPEC_VER (str): Test specification version number. Defaults to "".
+ FLOW_ID (str): Test flow ID. Defaults to "".
+ SETUP_ID (str): Test setup ID. Defaults to "".
+ DSGN_REV (str): Device design revision. Defaults to "".
+ ENG_ID (str): Engineering lot ID. Defaults to "".
+ ROM_COD (str): ROM code ID. Defaults to "".
+ SERL_NUM (str): Tester serial number. Defaults to "".
+ SUPR_NAM (str): Supervisor name or ID. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, SETUP_T)
+ obj.U_4.pack_into(obj.buffer, START_T)
+ obj.U_1.pack_into(obj.buffer, STAT_NUM)
+ obj.C_1.pack_into(obj.buffer, MODE_COD)
+ obj.C_1.pack_into(obj.buffer, RTST_COD)
+ obj.C_1.pack_into(obj.buffer, PROT_COD)
+ obj.U_2.pack_into(obj.buffer, BURN_TIM)
+ obj.C_1.pack_into(obj.buffer, CMOD_COD)
+ obj.C_n.pack_into(obj.buffer, LOT_ID)
+ obj.C_n.pack_into(obj.buffer, PART_TYP)
+ obj.C_n.pack_into(obj.buffer, NODE_NAM)
+ obj.C_n.pack_into(obj.buffer, TSTR_TYP)
+ obj.C_n.pack_into(obj.buffer, JOB_NAM)
+ obj.C_n.pack_into(obj.buffer, JOB_REV)
+ obj.C_n.pack_into(obj.buffer, SBLOT_ID)
+ obj.C_n.pack_into(obj.buffer, OPER_NAM)
+ obj.C_n.pack_into(obj.buffer, EXEC_TYP)
+ obj.C_n.pack_into(obj.buffer, EXEC_VER)
+ obj.C_n.pack_into(obj.buffer, TEST_COD)
+ obj.C_n.pack_into(obj.buffer, TST_TEMP)
+ obj.C_n.pack_into(obj.buffer, USER_TXT)
+ obj.C_n.pack_into(obj.buffer, AUX_FILE)
+ obj.C_n.pack_into(obj.buffer, PKG_TYP)
+ obj.C_n.pack_into(obj.buffer, FAMLY_ID)
+ obj.C_n.pack_into(obj.buffer, DATE_COD)
+ obj.C_n.pack_into(obj.buffer, FACIL_ID)
+ obj.C_n.pack_into(obj.buffer, FLOOR_ID)
+ obj.C_n.pack_into(obj.buffer, PROC_ID)
+ obj.C_n.pack_into(obj.buffer, OPER_FRQ)
+ obj.C_n.pack_into(obj.buffer, SPEC_NAM)
+ obj.C_n.pack_into(obj.buffer, SPEC_VER)
+ obj.C_n.pack_into(obj.buffer, FLOW_ID)
+ obj.C_n.pack_into(obj.buffer, SETUP_ID)
+ obj.C_n.pack_into(obj.buffer, DSGN_REV)
+ obj.C_n.pack_into(obj.buffer, ENG_ID)
+ obj.C_n.pack_into(obj.buffer, ROM_COD)
+ obj.C_n.pack_into(obj.buffer, SERL_NUM)
+ obj.C_n.pack_into(obj.buffer, SUPR_NAM)
+
+ self.write_fields(1, 10, write_fields)
+
+ def MRR(
+ self,
+ FINISH_T: int = 0,
+ DISP_COD: AlphaNumericLiteral = " ",
+ USR_DESC: str = "",
+ EXC_DESC: str = "",
+ ) -> None:
+ """Master Results Record (MRR)
+
+ The Master Results Record (MRR) is a logical extension of the Master Information
+ Record (MIR). The data can be thought of as belonging with the MIR, but it is not
+ available when the tester writes the MIR information. Each data stream must have
+ exactly one MRR as the last record in the data stream.
+
+ Note:
+ Must be the last record in the data stream.
+
+ Args:
+ FINISH_T (int): Date and time last part tested. Defaults to 0.
+ DISP_COD (AlphaNumericLiteral): Lot disposition code. Defaults to " ".
+ USR_DESC (str): Lot description supplied by user. Defaults to "".
+ EXC_DESC (str): Lot description supplied by exec. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, FINISH_T)
+ obj.C_1.pack_into(obj.buffer, DISP_COD)
+ obj.C_n.pack_into(obj.buffer, USR_DESC)
+ obj.C_n.pack_into(obj.buffer, EXC_DESC)
+
+ self.write_fields(1, 20, write_fields)
+
+ def PCR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ PART_CNT: int,
+ RTST_CNT: int = 4294967295,
+ ABRT_CNT: int = 4294967295,
+ GOOD_CNT: int = 4294967295,
+ FUNC_CNT: int = 4294967295,
+ ) -> None:
+ """Part Count Record (PCR)
+
+ Contains the part count totals for one or all test sites. Each data stream must have at
+ least one PCR to show the part count.
+
+ Note:
+ There must be at least one PCR in the file: either one summary PCR for all test sites
+ (HEAD_NUM= 255), or one PCR for each head/site combination, or both.
+ When data is being recorded in real time, this record will usually appear near the
+ end of the data stream.
+
+ Args:
+ HEAD_NUM (int): Test head number. If this PCR contains a summary of the part counts for
+ all test sites, this field must be set to 255
+ SITE_NUM (int): Test site number.
+ PART_CNT (int): Number of parts tested.
+ RTST_CNT (int): Number of parts retested. Defaults to 4294967295.
+ ABRT_CNT (int): Number of aborts during testing. Defaults to 4294967295.
+ GOOD_CNT (int): Number of good (passed) parts tested. Defaults to 4294967295.
+ FUNC_CNT (int): Number of functional parts tested. Defaults to 4294967295.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.U_4.pack_into(obj.buffer, PART_CNT)
+ obj.U_4.pack_into(obj.buffer, RTST_CNT)
+ obj.U_4.pack_into(obj.buffer, ABRT_CNT)
+ obj.U_4.pack_into(obj.buffer, GOOD_CNT)
+ obj.U_4.pack_into(obj.buffer, FUNC_CNT)
+
+ self.write_fields(1, 30, write_fields)
+
+ def HBR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ HBIN_NUM: int,
+ HBIN_CNT: int,
+ HBIN_PF: BinPassFailLiteral = " ",
+ HBIN_NAM: str = "",
+ ) -> None:
+ """Hardware Bin Record (HBR)
+
+ Stores a count of the parts "physically" placed in a particular bin after testing. (In
+ wafer testing, "physical" binning is not an actual transfer of the chip, but rather is
+ represented by a drop of ink or an entry in a wafer map file.) This bin count can be for
+ a single test site (when parallel testing) or a total for all test sites.
+
+ Note:
+ When data is being recorded in real time, this record usually appears near the end of
+ the data stream.
+
+ Args:
+ HEAD_NUM (int): Test head number. If this HBR contains a summary of the hardware bin
+ counts for all test sites, this field must be set to 255.
+ SITE_NUM (int): Test site number.
+ HBIN_NUM (int): Hardware bin number.
+ HBIN_CNT (int): Number of parts in bin.
+ HBIN_PF (BinPassFailLiteral): Pass/fail indication. Defaults to " ".
+ HBIN_NAM (str): Name of hardware bin. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.U_2.pack_into(obj.buffer, HBIN_NUM)
+ obj.U_4.pack_into(obj.buffer, HBIN_CNT)
+ obj.C_1.pack_into(obj.buffer, HBIN_PF)
+ obj.C_n.pack_into(obj.buffer, HBIN_NAM)
+
+ self.write_fields(1, 40, write_fields)
+
+ def SBR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ SBIN_NUM: int,
+ SBIN_CNT: int,
+ SBIN_PF: BinPassFailLiteral = " ",
+ SBIN_NAM: str = "",
+ ) -> None:
+ """Software Bin Record (SBR)
+
+ Stores a count of the parts associated with a particular logical bin after testing. This
+ bin count can be for a single test site (when parallel testing) or a total for all test sites.
+
+ Note:
+ One per software bin for each site. One per software bin for bin totals.
+ When data is being recorded in real time, this record usually appears near the
+ end of the data stream.
+
+ Args:
+ HEAD_NUM (int): Test head number. If this SBR contains a summary of the software bin
+ counts for all test sites, this field must be set to 255.
+ SITE_NUM (int): Test site number.
+ SBIN_NUM (int): Software bin number.
+ SBIN_CNT (int): Number of parts in bin.
+ SBIN_PF (BinPassFailLiteral): Pass/fail indication. Defaults to " ".
+ SBIN_NAM (str): Name of software bin. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.U_2.pack_into(obj.buffer, SBIN_NUM)
+ obj.U_4.pack_into(obj.buffer, SBIN_CNT)
+ obj.C_1.pack_into(obj.buffer, SBIN_PF)
+ obj.C_n.pack_into(obj.buffer, SBIN_NAM)
+
+ self.write_fields(1, 50, write_fields)
+
+ def PMR(
+ self,
+ PMR_INDX: int,
+ CHAN_TYP: int = 0,
+ CHAN_NAM: str = "",
+ PHY_NAM: str = "",
+ LOG_NAM: str = "",
+ HEAD_NUM: int = 1,
+ SITE_NUM: int = 1,
+ ) -> None:
+ """Pin Map Record (PMR)
+
+ Provides indexing of tester channel names, and maps them to physical and logical pin
+ names. Each PMR defines the information for a single channel/pin combination.
+
+ Note:
+ One per channel/pin combination used in the test program.
+ Reuse of a PMR index number is not permitted.
+ After the initial sequence and before the first PGR, PLR, FTR, or MPR that
+ uses this record's PMR_INDX value.
+
+ Args:
+ PMR_INDX (int): Unique index associated with pin. The range of legal PMR indexes is 1 - 32,767.
+ CHAN_TYP (int): Channel type. Defaults to 0.
+ CHAN_NAM (str): Channel name. Defaults to "".
+ PHY_NAM (str): Physical name of pin. Defaults to "".
+ LOG_NAM (str): Logical name of pin. Defaults to "".
+ HEAD_NUM (int): Head number associated with channel. Defaults to 1.
+ SITE_NUM (int): Site number associated with channel. Defaults to 1.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_2.pack_into(obj.buffer, PMR_INDX)
+ obj.U_2.pack_into(obj.buffer, CHAN_TYP)
+ obj.C_n.pack_into(obj.buffer, CHAN_NAM)
+ obj.C_n.pack_into(obj.buffer, PHY_NAM)
+ obj.C_n.pack_into(obj.buffer, LOG_NAM)
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+
+ self.write_fields(1, 60, write_fields)
+
+ def PGR(
+ self,
+ GRP_INDX: int,
+ GRP_NAM: str = "",
+ INDX_CNT: int = 0,
+ PMR_INDX: Sequence[int] = tuple(),
+ ) -> None:
+ """Pin Group Record (PGR)
+
+ Associates a name with a group of pins.
+
+ Note:
+ After all the PMRs whose PMR index values are listed in the PMR_INDX array of this
+ record; and before the first PLR that uses this recordβs GRP_INDX value.
+
+ Args:
+ GRP_INDX (int): Unique index associated with pin group. The range of legal group index numbers
+ is 32,768 - 65,535.
+ GRP_NAM (str): Name of pin group. Defaults to "".
+ INDX_CNT (int): Count (k) of PMR indexes. Defaults to 0.
+ PMR_INDX (Sequence[int]): Array of indexes for pins in the group. Defaults to tuple().
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_2.pack_into(obj.buffer, GRP_INDX)
+ obj.C_n.pack_into(obj.buffer, GRP_NAM)
+ obj.U_2.pack_into(obj.buffer, INDX_CNT)
+ obj.kxU_2.pack_into(obj.buffer, PMR_INDX, INDX_CNT)
+
+ self.write_fields(1, 62, write_fields)
+
+ def PLR(
+ self,
+ GRP_CNT: int = 0,
+ GRP_INDX: Sequence[int] = tuple(),
+ GRP_MODE: Sequence[int] = tuple(),
+ GRP_RADX: Sequence[int] = tuple(),
+ PGM_CHAR: Sequence[str] = tuple(),
+ RTN_CHAR: Sequence[str] = tuple(),
+ PGM_CHAL: Sequence[str] = tuple(),
+ RTN_CHAL: Sequence[str] = tuple(),
+ ) -> None:
+ """Pin List Record (PLR)
+
+ Defines the current display radix and operating mode for a pin or pin group.
+
+ Note:
+ After all the PMRs and PGRs whose PMR index values and pin group index values are
+ listed in the GRP_INDX array of this record; and before the first FTR that references pins
+ or pin groups whose modes are defined in this record.
+
+ Args:
+ GRP_CNT (int): Count (k) of pins or pin groups. Defaults to 0.
+ GRP_INDX (Sequence[int]): Array of pin or pin group indexes. Defaults to tuple().
+ GRP_MODE (Sequence[int]): Operating mode of pin group. Defaults to tuple().
+ GRP_RADX (Sequence[int]): Display radix of pin group. Defaults to tuple().
+ PGM_CHAR (Sequence[str]): Program state encoding characters. Defaults to tuple().
+ RTN_CHAR (Sequence[str]): Return state encoding characters. Defaults to tuple().
+ PGM_CHAL (Sequence[str]): Program state encoding characters. Defaults to tuple().
+ RTN_CHAL (Sequence[str]): Return state encoding characters. Defaults to tuple().
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_2.pack_into(obj.buffer, GRP_CNT)
+ obj.kxU_2.pack_into(obj.buffer, GRP_INDX, size=GRP_CNT)
+ obj.kxU_2.pack_into(obj.buffer, GRP_MODE, size=GRP_CNT)
+ obj.kxU_1.pack_into(obj.buffer, GRP_RADX, size=GRP_CNT)
+ obj.kxC_n.pack_into(obj.buffer, PGM_CHAR, size=GRP_CNT)
+ obj.kxC_n.pack_into(obj.buffer, RTN_CHAR, size=GRP_CNT)
+ obj.kxC_n.pack_into(obj.buffer, PGM_CHAL, size=GRP_CNT)
+ obj.kxC_n.pack_into(obj.buffer, RTN_CHAL, size=GRP_CNT)
+
+ self.write_fields(1, 63, write_fields)
+
+ def RDR(
+ self,
+ NUM_BINS: int = 0,
+ RTST_BIN: Sequence[int] = tuple(),
+ ) -> None:
+ """Retest Data Record (RDR)
+
+ Signals that the data in this STDF file is for retested parts. The data in this record,
+ combined with information in the MIR, tells data filtering programs what data to
+ replace when processing retest data.
+
+ Note:
+ If this record is used, it must appear immediately after the Master Information Record (MIR).
+
+ Args:
+ NUM_BINS (int): Number (k) of bins being retested. Defaults to 0.
+ RTST_BIN (Sequence[int]): Array of retest bin numbers. Defaults to tuple().
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_2.pack_into(obj.buffer, NUM_BINS)
+ obj.kxU_2.pack_into(obj.buffer, RTST_BIN, size=NUM_BINS)
+
+ self.write_fields(1, 70, write_fields)
+
+ def SDR(
+ self,
+ HEAD_NUM: int,
+ SITE_GRP: int,
+ SITE_CNT: int = 0,
+ SITE_NUM: Sequence[int] = tuple(),
+ HAND_TYP: str = "",
+ HAND_ID: str = "",
+ CARD_TYP: str = "",
+ CARD_ID: str = "",
+ LOAD_TYP: str = "",
+ LOAD_ID: str = "",
+ DIB_TYP: str = "",
+ DIB_ID: str = "",
+ CABL_TYP: str = "",
+ CABL_ID: str = "",
+ CONT_TYP: str = "",
+ CONT_ID: str = "",
+ LASR_TYP: str = "",
+ LASR_ID: str = "",
+ EXTR_TYP: str = "",
+ EXTR_ID: str = "",
+ ) -> None:
+ """Site Description Record (SDR)
+
+ Contains the configuration information for one or more test sites, connected to one test
+ head, that compose a site group.
+
+ Note:
+ One for each site or group of sites that is differently configured.
+ Immediately after the MIR and RDR (if an RDR is used).
+
+ Args:
+ HEAD_NUM (int): Test head number..
+ SITE_GRP (int): Site group number..
+ SITE_CNT (int): Number (k) of test sites in site group. Defaults to 0.
+ SITE_NUM (Sequence[int]): Array of test site numbers. Defaults to tuple().
+ HAND_TYP (str): Handler or probe type. Defaults to "".
+ HAND_ID (str): Handler or prober ID. Defaults to "".
+ CARD_TYP (str): Probe card type. Defaults to "".
+ CARD_ID (str): Probe card ID. Defaults to "".
+ LOAD_TYP (str): Load board type. Defaults to "".
+ LOAD_ID (str): Load board ID. Defaults to "".
+ DIB_TYP (str): DIB board type. Defaults to "".
+ DIB_ID (str): DIB board ID. Defaults to "".
+ CABL_TYP (str): Interface cable type. Defaults to "".
+ CABL_ID (str): Interface cable ID. Defaults to "".
+ CONT_TYP (str): Handler contactor type. Defaults to "".
+ CONT_ID (str): Handler contactor ID. Defaults to "".
+ LASR_TYP (str): Laser type. Defaults to "".
+ LASR_ID (str): Laser ID. Defaults to "".
+ EXTR_TYP (str): Extra equipment type field. Defaults to "".
+ EXTR_ID (str): Extra equipment ID. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_GRP)
+ obj.U_1.pack_into(obj.buffer, SITE_CNT)
+ obj.kxU_1.pack_into(obj.buffer, SITE_NUM, size=SITE_CNT)
+ obj.C_n.pack_into(obj.buffer, HAND_TYP)
+ obj.C_n.pack_into(obj.buffer, HAND_ID)
+ obj.C_n.pack_into(obj.buffer, CARD_TYP)
+ obj.C_n.pack_into(obj.buffer, CARD_ID)
+ obj.C_n.pack_into(obj.buffer, LOAD_TYP)
+ obj.C_n.pack_into(obj.buffer, LOAD_ID)
+ obj.C_n.pack_into(obj.buffer, DIB_TYP)
+ obj.C_n.pack_into(obj.buffer, DIB_ID)
+ obj.C_n.pack_into(obj.buffer, CABL_TYP)
+ obj.C_n.pack_into(obj.buffer, CABL_ID)
+ obj.C_n.pack_into(obj.buffer, CONT_TYP)
+ obj.C_n.pack_into(obj.buffer, CONT_ID)
+ obj.C_n.pack_into(obj.buffer, LASR_TYP)
+ obj.C_n.pack_into(obj.buffer, LASR_ID)
+ obj.C_n.pack_into(obj.buffer, EXTR_TYP)
+ obj.C_n.pack_into(obj.buffer, EXTR_ID)
+
+ self.write_fields(1, 80, write_fields)
+
+ def WIR(
+ self,
+ HEAD_NUM: int,
+ START_T: int,
+ SITE_GRP: int = 255,
+ WAFER_ID: str = "",
+ ) -> None:
+ """Wafer Information Record (WIR)
+
+ Acts mainly as a marker to indicate where testing of a particular wafer begins for each
+ wafer tested by the job plan. The WIR and the Wafer Results Record (WRR) bracket all
+ the stored information pertaining to one tested wafer.
+
+ Note:
+ One per wafer tested.
+ Sent before testing each wafer.
+
+ Args:
+ HEAD_NUM (int): Test head number.
+ START_T (int): Date and time first part tested.
+ SITE_GRP (int): Site group number. If this information is not known, or the tester
+ does not support the concept of site groups, this field should be set to 255.
+ WAFER_ID (str): Wafer ID. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_GRP)
+ obj.U_4.pack_into(obj.buffer, START_T)
+ obj.C_n.pack_into(obj.buffer, WAFER_ID)
+
+ self.write_fields(2, 10, write_fields)
+
+ def WRR(
+ self,
+ HEAD_NUM: int,
+ FINISH_T: int,
+ PART_CNT: int,
+ SITE_GRP: int = 255,
+ RTST_CNT: int = 4294967295,
+ ABRT_CNT: int = 4294967295,
+ GOOD_CNT: int = 4294967295,
+ FUNC_CNT: int = 4294967295,
+ WAFER_ID: str = "",
+ FABWF_ID: str = "",
+ FRAME_ID: str = "",
+ MASK_ID: str = "",
+ USR_DESC: str = "",
+ EXC_DESC: str = "",
+ ) -> None:
+ """Wafer Results Record (WRR)
+
+ Contains the result information relating to each wafer tested by the job plan. The WRR
+ and the Wafer Information Record (WIR) bracket all the stored information pertaining
+ to one tested wafer.
+
+ Note:
+ Anywhere in the data stream after the corresponding WIR. Sent after testing each wafer.
+
+ Args:
+ HEAD_NUM (int): Test head number.
+ FINISH_T (int): Date and time last part tested.
+ PART_CNT (int): Number of parts tested.
+ SITE_GRP (int): Site group number. Defaults to 255.
+ RTST_CNT (int): Number of parts retested. Defaults to 4294967295.
+ ABRT_CNT (int): Number of aborts during testing. Defaults to 4294967295.
+ GOOD_CNT (int): Number of good (passed) parts tested. Defaults to 4294967295.
+ FUNC_CNT (int): Number of functional parts tested. Defaults to 4294967295.
+ WAFER_ID (str): Wafer ID. Defaults to "".
+ FABWF_ID (str): Fab wafer ID. Defaults to "".
+ FRAME_ID (str): Wafer frame ID. Defaults to "".
+ MASK_ID (str): Wafer mask ID. Defaults to "".
+ USR_DESC (str): Wafer description supplied by user. Defaults to "".
+ EXC_DESC (str): Wafer description supplied by exec. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_GRP)
+ obj.U_4.pack_into(obj.buffer, FINISH_T)
+ obj.U_4.pack_into(obj.buffer, PART_CNT)
+ obj.U_4.pack_into(obj.buffer, RTST_CNT)
+ obj.U_4.pack_into(obj.buffer, ABRT_CNT)
+ obj.U_4.pack_into(obj.buffer, GOOD_CNT)
+ obj.U_4.pack_into(obj.buffer, FUNC_CNT)
+ obj.C_n.pack_into(obj.buffer, WAFER_ID)
+ obj.C_n.pack_into(obj.buffer, FABWF_ID)
+ obj.C_n.pack_into(obj.buffer, FRAME_ID)
+ obj.C_n.pack_into(obj.buffer, MASK_ID)
+ obj.C_n.pack_into(obj.buffer, USR_DESC)
+ obj.C_n.pack_into(obj.buffer, EXC_DESC)
+
+ self.write_fields(2, 20, write_fields)
+
+ def WCR(
+ self,
+ WAFR_SIZ: float = 0.0,
+ DIE_HT: float = 0.0,
+ DIE_WID: float = 0.0,
+ WF_UNITS: Literal[0, 1, 2, 3, 4] = 0,
+ WF_FLAT: Literal["U", "D", "L", "R", " "] = " ",
+ CENTER_X: int = -32768,
+ CENTER_Y: int = -32768,
+ POS_X: Literal["L", "R", " "] = " ",
+ POS_Y: Literal["U", "D", " "] = " ",
+ ) -> None:
+ """Wafer Configuration Record (WCR)
+
+ Contains the configuration information for the wafers tested by the job plan. The
+ WCR provides the dimensions and orientation information for all wafers and dice
+ in the lot.
+
+ Note:
+ One per STDF file (used only if wafer testing).
+
+ Args:
+ WAFR_SIZ (float): Diameter of wafer in WF_UNITS. Defaults to 0.0.
+ DIE_HT (float): Height of die in WF_UNITS. Defaults to 0.0.
+ DIE_WID (float): Width of die in WF_UNITS. Defaults to 0.0.
+ WF_UNITS (Literal[0, 1, 2, 3, 4]): Units for wafer and die dimensions. 0 = Unknown units,
+ 1 = Units are in inches, 2 = Units are in centimeters, 3 = Units are in millimeters,
+ 4 = Units are in mils. Defaults to 0.
+ WF_FLAT (Literal["U", "D", "L", "R", " "]): Orientation of wafer flat. "U" = Up, "D" = Down,
+ "L" = Left, "R" = Right, " " = Unknown. Defaults to " ".
+ CENTER_X (int): X coordinate of center die on wafer. Defaults to -32768.
+ CENTER_Y (int): Y coordinate of center die on wafer. Defaults to -32768.
+ POS_X (Literal["L", "R", " "]): Positive X direction of wafer. "L" = Left, "R" = Right, " "
+ = Unknown. Defaults to " "
+ POS_Y (Literal["U", "D", " "]): Positive Y direction of wafer. "U" = Up, "D" = Down, " " =
+ Unknown. Defaults to " ".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.R_4.pack_into(obj.buffer, WAFR_SIZ)
+ obj.R_4.pack_into(obj.buffer, DIE_HT)
+ obj.R_4.pack_into(obj.buffer, DIE_WID)
+ obj.U_1.pack_into(obj.buffer, WF_UNITS)
+ obj.C_1.pack_into(obj.buffer, WF_FLAT)
+ obj.I_2.pack_into(obj.buffer, CENTER_X)
+ obj.I_2.pack_into(obj.buffer, CENTER_Y)
+ obj.C_1.pack_into(obj.buffer, POS_X)
+ obj.C_1.pack_into(obj.buffer, POS_Y)
+
+ self.write_fields(2, 30, write_fields)
+
+ def PIR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ ) -> None:
+ """Part Information Record (PIR)
+
+ Acts as a marker to indicate where testing of a particular part begins for each part
+ tested by the test program. The PIR and the Part Results Record (PRR) bracket all the
+ stored information pertaining to one tested part.
+
+ Note:
+ One per part tested.
+ Sent before testing each part.
+
+ Args:
+ HEAD_NUM (int): Test head number.
+ SITE_NUM (int): Test site number.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+
+ self.write_fields(5, 10, write_fields)
+
+ def PRR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ PART_FLG: bytes,
+ NUM_TEST: int,
+ HARD_BIN: int,
+ SOFT_BIN: int = 65535,
+ X_COORD: int = -32768,
+ Y_COORD: int = -32768,
+ TEST_T: int = 0,
+ PART_ID: str = "",
+ PART_TXT: str = "",
+ PART_FIX: bytes = b"",
+ ) -> None:
+ """Part Results Record (PRR)
+
+ Contains the result information relating to each part tested by the test program. The
+ PRR and the Part Information Record (PIR) bracket all the stored information
+ pertaining to one tested part.
+
+ Note:
+ One per part tested.
+ Sent after completion of testing each part.
+
+ Args:
+ HEAD_NUM (int): Test head number.
+ SITE_NUM (int): Test site number.
+ PART_FLG (bytes): Part information flag.
+ NUM_TEST (int): Number of tests executed.
+ HARD_BIN (int): Hardware bin number.
+ SOFT_BIN (int): Software bin number. Defaults to 65535.
+ X_COORD (int): (Wafer) X coordinate. Defaults to -32768. Have legal values in the
+ range -32767 to 32767. A missing value is indicated by the value -32768.
+ Y_COORD (int): (Wafer) Y coordinate. Defaults to -32768. Have legal values in the
+ range -32767 to 32767. A missing value is indicated by the value -32768.
+ TEST_T (int): Elapsed test time in milliseconds. Defaults to 0.
+ PART_ID (str): Part identification. Defaults to "".
+ PART_TXT (str): Part description text. Defaults to "".
+ PART_FIX (bytes): Part repair information. Defaults to b"".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.B_1.pack_into(obj.buffer, PART_FLG)
+ obj.U_2.pack_into(obj.buffer, NUM_TEST)
+ obj.U_2.pack_into(obj.buffer, HARD_BIN)
+ obj.U_2.pack_into(obj.buffer, SOFT_BIN)
+ obj.I_2.pack_into(obj.buffer, X_COORD)
+ obj.I_2.pack_into(obj.buffer, Y_COORD)
+ obj.U_4.pack_into(obj.buffer, TEST_T)
+ obj.C_n.pack_into(obj.buffer, PART_ID)
+ obj.C_n.pack_into(obj.buffer, PART_TXT)
+ obj.B_n.pack_into(obj.buffer, PART_FIX)
+
+ self.write_fields(5, 20, write_fields)
+
+ def TSR(
+ self,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ TEST_NUM: int,
+ OPT_FLAG: bytes,
+ TEST_TYP: TestTypeLiteral = " ",
+ EXEC_CNT: int = 4294967295,
+ FAIL_CNT: int = 4294967295,
+ ALRM_CNT: int = 4294967295,
+ TEST_NAM: str = "",
+ SEQ_NAME: str = "",
+ TEST_LBL: str = "",
+ TEST_TIM: float = 0.0,
+ TEST_MIN: float = 0.0,
+ TEST_MAX: float = 0.0,
+ TST_SUMS: float = 0.0,
+ TST_SQRS: float = 0.0,
+ ) -> None:
+ """Test Synopsis Record (TSR)
+
+ Contains the test execution and failure counts for one parametric or functional test in
+ the test program. Also contains static information, such as test name.
+
+ Note:
+ One for each test executed in the test program.
+ When test data is being generated in real-time, these records will appear after the last PRR.
+
+ Args:
+ HEAD_NUM (int): Test head number. If this TSR contains a summary of the test counts for all
+ test sites, this field must be set to 255.
+ SITE_NUM (int): Test site number.
+ TEST_NUM (int): Test number.
+ OPT_FLAG (bytes): Optional data flag.
+ TEST_TYP (AlphaNumericLiteral): Test type. Defaults to " ".
+ EXEC_CNT (int): Number of test executions. Defaults to 4294967295.
+ FAIL_CNT (int): Number of test failures. Defaults to 4294967295.
+ ALRM_CNT (int): Number of alarmed tests. Defaults to 4294967295.
+ TEST_NAM (str): Test name. Defaults to "".
+ SEQ_NAME (str): Sequencer (program segment/flow) name. Defaults to "".
+ TEST_LBL (str): Test label or text. Defaults to "".
+ TEST_TIM (float): Average test execution time in seconds. Defaults to 0.0.
+ TEST_MIN (float): Lowest test result value. Defaults to 0.0.
+ TEST_MAX (float): Highest test result value. Defaults to 0.0.
+ TST_SUMS (float): Sum of test result values. Defaults to 0.0.
+ TST_SQRS (float): Sum of squares of test result values. Defaults to 0.0.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.C_1.pack_into(obj.buffer, TEST_TYP)
+ obj.U_4.pack_into(obj.buffer, TEST_NUM)
+ obj.U_4.pack_into(obj.buffer, EXEC_CNT)
+ obj.U_4.pack_into(obj.buffer, FAIL_CNT)
+ obj.U_4.pack_into(obj.buffer, ALRM_CNT)
+ obj.C_n.pack_into(obj.buffer, TEST_NAM)
+ obj.C_n.pack_into(obj.buffer, SEQ_NAME)
+ obj.C_n.pack_into(obj.buffer, TEST_LBL)
+ obj.B_1.pack_into(obj.buffer, OPT_FLAG)
+ obj.R_4.pack_into(obj.buffer, TEST_TIM)
+ obj.R_4.pack_into(obj.buffer, TEST_MIN)
+ obj.R_4.pack_into(obj.buffer, TEST_MAX)
+ obj.R_4.pack_into(obj.buffer, TST_SUMS)
+ obj.R_4.pack_into(obj.buffer, TST_SQRS)
+
+ self.write_fields(10, 30, write_fields)
+
+ def PTR(
+ self,
+ TEST_NUM: int,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ TEST_FLG: bytes,
+ PARM_FLG: bytes,
+ OPT_FLAG: bytes,
+ RESULT: float = 0.0,
+ TEST_TXT: str = "",
+ ALARM_ID: str = "",
+ RES_SCAL: int = 0,
+ LLM_SCAL: int = 0,
+ HLM_SCAL: int = 0,
+ LO_LIMIT: float = 0.0,
+ HI_LIMIT: float = 0.0,
+ UNITS: str = "",
+ C_RESFMT: str = "",
+ C_LLMFMT: str = "",
+ C_HLMFMT: str = "",
+ LO_SPEC: float = 0.0,
+ HI_SPEC: float = 0.0,
+ ) -> None:
+ """Parametric Test Record (PTR)
+
+ Contains the results of a single execution of a parametric test in the test program. The
+ first occurrence of this record also establishes the default values for all semi-static
+ information about the test, such as limits, units, and scaling.
+
+ Note:
+ One per parametric test execution.
+
+ Args:
+ TEST_NUM (int): Test number.
+ HEAD_NUM (int): Test head number.
+ SITE_NUM (int): Test site number.
+ TEST_FLG (bytes): Test flags (fail, alarm, etc.).
+ PARM_FLG (bytes): Parametric test flags (drift, etc.).
+ OPT_FLAG (bytes): Optional data flag.
+ RESULT (float): Test result. Defaults to 0.0.
+ TEST_TXT (str): Test description text or label. Defaults to "".
+ ALARM_ID (str): Name of alarm. Defaults to "".
+ RES_SCAL (int): Test results scaling exponent. Defaults to 0.
+ LLM_SCAL (int): Low limit scaling exponent. Defaults to 0.
+ HLM_SCAL (int): High limit scaling exponent. Defaults to 0.
+ LO_LIMIT (float): Low test limit value. Defaults to 0.0.
+ HI_LIMIT (float): High test limit value. Defaults to 0.0.
+ UNITS (str): Test units. Defaults to "".
+ C_RESFMT (str): ANSI C result format string. Defaults to "".
+ C_LLMFMT (str): ANSI C low limit format string. Defaults to "".
+ C_HLMFMT (str): ANSI C high limit format string. Defaults to "".
+ LO_SPEC (float): Low specification limit value. Defaults to 0.0.
+ HI_SPEC (float): High specification limit value. Defaults to 0.0.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, TEST_NUM)
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.B_1.pack_into(obj.buffer, TEST_FLG)
+ obj.B_1.pack_into(obj.buffer, PARM_FLG)
+ obj.R_4.pack_into(obj.buffer, RESULT)
+ obj.C_n.pack_into(obj.buffer, TEST_TXT)
+ obj.C_n.pack_into(obj.buffer, ALARM_ID)
+ obj.B_1.pack_into(obj.buffer, OPT_FLAG)
+ obj.I_1.pack_into(obj.buffer, RES_SCAL)
+ obj.I_1.pack_into(obj.buffer, LLM_SCAL)
+ obj.I_1.pack_into(obj.buffer, HLM_SCAL)
+ obj.R_4.pack_into(obj.buffer, LO_LIMIT)
+ obj.R_4.pack_into(obj.buffer, HI_LIMIT)
+ obj.C_n.pack_into(obj.buffer, UNITS)
+ obj.C_n.pack_into(obj.buffer, C_RESFMT)
+ obj.C_n.pack_into(obj.buffer, C_LLMFMT)
+ obj.C_n.pack_into(obj.buffer, C_HLMFMT)
+ obj.R_4.pack_into(obj.buffer, LO_SPEC)
+ obj.R_4.pack_into(obj.buffer, HI_SPEC)
+
+ self.write_fields(15, 10, write_fields)
+
+ def MPR(
+ self,
+ TEST_NUM: int,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ TEST_FLG: bytes,
+ PARM_FLG: bytes,
+ OPT_FLAG: bytes,
+ RTN_ICNT: int = 0,
+ RSLT_CNT: int = 0,
+ RTN_STAT: Sequence[int] = tuple(),
+ RTN_RSLT: Sequence[float] = tuple(),
+ TEST_TXT: str = "",
+ ALARM_ID: str = "",
+ RES_SCAL: int = 0,
+ LLM_SCAL: int = 0,
+ HLM_SCAL: int = 0,
+ LO_LIMIT: float = 0.0,
+ HI_LIMIT: float = 0.0,
+ START_IN: float = 0.0,
+ INCR_IN: float = 0.0,
+ RTN_INDX: Sequence[int] = tuple(),
+ UNITS: str = "",
+ UNITS_IN: str = "",
+ C_RESFMT: str = "",
+ C_LLMFMT: str = "",
+ C_HLMFMT: str = "",
+ LO_SPEC: float = 0.0,
+ HI_SPEC: float = 0.0,
+ ) -> None:
+ """Multiple-Result Parametric Record (MPR)
+
+ Contains the results of a single execution of a parametric test in the test program
+ where that test returns multiple values. The first occurrence of this record also
+ establishes the default values for all semi-static information about the test, such as
+ limits, units, and scaling.
+
+ Note:
+ One per multiple-result parametric test execution.
+
+ Args:
+ TEST_NUM (int): Test number.
+ HEAD_NUM (int): Test head number.
+ SITE_NUM (int): Test site number.
+ TEST_FLG (bytes): Test flags (fail, alarm, etc.).
+ PARM_FLG (bytes): Parametric test flags (drift, etc.).
+ OPT_FLAG (bytes): Optional data flag.
+ RTN_ICNT (int): Count (j) of PMR indexes. Defaults to 0.
+ RSLT_CNT (int): Count (k) of returned results. Defaults to 0.
+ RTN_STAT (Sequence[int]): Array of returned states. Defaults to tuple().
+ RTN_RSLT (Sequence[float]): Array of returned results. Defaults to tuple().
+ TEST_TXT (str): Descriptive text or label. Defaults to "".
+ ALARM_ID (str): Name of alarm. Defaults to "".
+ RES_SCAL (int): Test result scaling exponent. Defaults to 0.
+ LLM_SCAL (int): Test low limit scaling exponent. Defaults to 0.
+ HLM_SCAL (int): Test high limit scaling exponent. Defaults to 0.
+ LO_LIMIT (float): Test low limit value. Defaults to 0.0.
+ HI_LIMIT (float): Test high limit value. Defaults to 0.0.
+ START_IN (float): Starting input value (condition). Defaults to 0.0.
+ INCR_IN (float): Increment of input condition. Defaults to 0.0.
+ RTN_INDX (Sequence[int]): Array of PMR indexes. Defaults to None.
+ UNITS (str): Units of returned results. Defaults to "".
+ UNITS_IN (str): Input condition units. Defaults to "".
+ C_RESFMT (str): ANSI C result format string. Defaults to "".
+ C_LLMFMT (str): ANSI C low limit format string. Defaults to "".
+ C_HLMFMT (str): ANSI C high limit format string. Defaults to "".
+ LO_SPEC (float): Low specification limit value. Defaults to 0.0.
+ HI_SPEC (float): High specification limit value. Defaults to 0.0.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, TEST_NUM)
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.B_1.pack_into(obj.buffer, TEST_FLG)
+ obj.B_1.pack_into(obj.buffer, PARM_FLG)
+ obj.U_2.pack_into(obj.buffer, RTN_ICNT)
+ obj.U_2.pack_into(obj.buffer, RSLT_CNT)
+ # obj.kx_N1.pack_into(obj.buffer, RTN_STAT) # TODO: fix this
+ obj.kxR_4.pack_into(obj.buffer, RTN_RSLT, size=RSLT_CNT)
+ obj.C_n.pack_into(obj.buffer, TEST_TXT)
+ obj.C_n.pack_into(obj.buffer, ALARM_ID)
+ obj.B_1.pack_into(obj.buffer, OPT_FLAG)
+ obj.I_1.pack_into(obj.buffer, RES_SCAL)
+ obj.I_1.pack_into(obj.buffer, LLM_SCAL)
+ obj.I_1.pack_into(obj.buffer, HLM_SCAL)
+ obj.R_4.pack_into(obj.buffer, LO_LIMIT)
+ obj.R_4.pack_into(obj.buffer, HI_LIMIT)
+ obj.R_4.pack_into(obj.buffer, START_IN)
+ obj.R_4.pack_into(obj.buffer, INCR_IN)
+ obj.kxU_2.pack_into(obj.buffer, RTN_INDX, size=RTN_ICNT)
+ obj.C_n.pack_into(obj.buffer, UNITS)
+ obj.C_n.pack_into(obj.buffer, UNITS_IN)
+ obj.C_n.pack_into(obj.buffer, C_RESFMT)
+ obj.C_n.pack_into(obj.buffer, C_LLMFMT)
+ obj.C_n.pack_into(obj.buffer, C_HLMFMT)
+ obj.R_4.pack_into(obj.buffer, LO_SPEC)
+ obj.R_4.pack_into(obj.buffer, HI_SPEC)
+
+ self.write_fields(15, 15, write_fields)
+
+ def FTR(
+ self,
+ TEST_NUM: int,
+ HEAD_NUM: int,
+ SITE_NUM: int,
+ TEST_FLG: bytes,
+ OPT_FLAG: bytes,
+ CYCL_CNT: int = 0,
+ REL_VADR: int = 0,
+ REPT_CNT: int = 0,
+ NUM_FAIL: int = 0, #
+ XFAIL_AD: int = 0,
+ YFAIL_AD: int = 0,
+ VECT_OFF: int = 0,
+ RTN_ICNT: int = 0,
+ PGM_ICNT: int = 0,
+ RTN_INDX: Sequence[int] = tuple(),
+ RTN_STAT: Sequence[int] = tuple(),
+ PGM_INDX: Sequence[int] = tuple(),
+ PGM_STAT: Sequence[int] = tuple(),
+ FAIL_PIN: bytes = b"",
+ VECT_NAM: str = "",
+ TIME_SET: str = "",
+ OP_CODE: str = "",
+ TEST_TXT: str = "",
+ ALARM_ID: str = "",
+ PROG_TXT: str = "",
+ RSLT_TXT: str = "",
+ PATG_NUM: int = 255,
+ SPIN_MAP: bytes = b"",
+ ) -> None:
+ """Functional Test Record (FTR)
+
+ Contains the results of the single execution of a functional test in the test program. The
+ first occurrence of this record also establishes the default values for all semi-static
+ information about the test.
+
+ Note:
+ One or more for each execution of a functional test.
+
+ Args:
+ TEST_NUM (int): Test number.
+ HEAD_NUM (int): Test head number.
+ SITE_NUM (int): Test site number.
+ TEST_FLG (int): Test flags (fail, alarm, etc.).
+ OPT_FLAG (int): Optional data flag.
+ CYCL_CNT (int): Cycle count of vector. Defaults to 0.
+ REL_VADR (int): Relative vector address. Defaults to 0.
+ REPT_CNT (int): Repeat count of vector. Defaults to 0.
+ NUM_FAIL (int): Number of pins with 1 or more failures. Defaults to 0.
+ XFAIL_AD (int): X logical device failure address. Defaults to 0.
+ YFAIL_AD (int): Y logical device failure address. Defaults to 0.
+ VECT_OFF (int): Offset from vector of interest. Defaults to 0.
+ RTN_ICNT (int): Count (j) of return data PMR indexes. Defaults to 0.
+ PGM_ICNT (int): Count (k) of programmed state indexes. Defaults to 0.
+ RTN_INDX (Sequence[int]): Array of return data PMR indexes. Defaults to tuple().
+ RTN_STAT (Sequence[int]): Array of returned states. Defaults to tuple().
+ PGM_INDX (Sequence[int]): Array of programmed state indexes. Defaults to tuple().
+ PGM_STAT (Sequence[int]): Array of programmed states. Defaults to tuple().
+ FAIL_PIN (bytes): Failing pin bitfield. Defaults to b"".
+ VECT_NAM (str): Vector module pattern name. Defaults to "".
+ TIME_SET (str): Time set name. Defaults to "".
+ OP_CODE (str): Vector Op Code. Defaults to "".
+ TEST_TXT (str): Descriptive text or label. Defaults to "".
+ ALARM_ID (str): Name of alarm. Defaults to "".
+ PROG_TXT (str): Additional programmed information. Defaults to "".
+ RSLT_TXT (str): Additional result information. Defaults to "".
+ PATG_NUM (int): Pattern generator number. Defaults to 255.
+ SPIN_MAP (bytes): Bit map of enabled comparators. Defaults to b"".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_4.pack_into(obj.buffer, TEST_NUM)
+ obj.U_1.pack_into(obj.buffer, HEAD_NUM)
+ obj.U_1.pack_into(obj.buffer, SITE_NUM)
+ obj.B_1.pack_into(obj.buffer, TEST_FLG)
+ obj.B_1.pack_into(obj.buffer, OPT_FLAG)
+ obj.U_4.pack_into(obj.buffer, CYCL_CNT)
+ obj.U_4.pack_into(obj.buffer, REL_VADR)
+ obj.U_4.pack_into(obj.buffer, REPT_CNT)
+ obj.U_4.pack_into(obj.buffer, NUM_FAIL)
+ obj.I_4.pack_into(obj.buffer, XFAIL_AD)
+ obj.I_4.pack_into(obj.buffer, YFAIL_AD)
+ obj.I_2.pack_into(obj.buffer, VECT_OFF)
+ obj.U_2.pack_into(obj.buffer, RTN_ICNT)
+ obj.U_2.pack_into(obj.buffer, PGM_ICNT)
+ obj.kxU_2.pack_into(obj.buffer, RTN_INDX, size=RTN_ICNT)
+ # obj.kx_N1.pack_into(obj.buffer, RTN_STAT, size=RTN_ICNT) # TODO: fix this
+ obj.kxU_2.pack_into(obj.buffer, PGM_INDX, size=PGM_ICNT)
+ # obj.kx_N1.pack_into(obj.buffer, PGM_STAT, size=PGM_ICNT) # TODO: fix this
+ # obj.D_n.pack_into(obj.buffer, FAIL_PIN) # TODO: fix this
+ obj.C_n.pack_into(obj.buffer, VECT_NAM)
+ obj.C_n.pack_into(obj.buffer, TIME_SET)
+ obj.C_n.pack_into(obj.buffer, OP_CODE)
+ obj.C_n.pack_into(obj.buffer, TEST_TXT)
+ obj.C_n.pack_into(obj.buffer, ALARM_ID)
+ obj.C_n.pack_into(obj.buffer, PROG_TXT)
+ obj.C_n.pack_into(obj.buffer, RSLT_TXT)
+ obj.U_1.pack_into(obj.buffer, PATG_NUM)
+ # obj.D_n.pack_into(obj.buffer, SPIN_MAP) # TODO: fix this
+
+ self.write_fields(15, 20, write_fields)
+
+ def BPS(
+ self,
+ SEQ_NAME: str = "",
+ ) -> None:
+ """Begin Program Section Record (BPS)
+
+ Marks the beginning of a new program section (or sequencer) in the job plan.
+
+ Note:
+ Anywhere after the PIR and before the PRR.
+
+ Args:
+ SEQ_NAME (str): Program section (or sequencer) name. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.C_n.pack_into(obj.buffer, SEQ_NAME)
+
+ self.write_fields(20, 10, write_fields)
+
+ def EPS(
+ self,
+ ) -> None:
+ """End Program Section Record (EPS)
+
+ Marks the end of the current program section (or sequencer) in the job plan.
+
+ Note:
+ Following the corresponding BPS and before the PRR in the data stream.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ return
+
+ self.write_fields(20, 20, write_fields)
+
+ def GDR(
+ self,
+ FLD_CNT: int = 0,
+ GEN_DATA: Sequence[Any] = tuple(),
+ ) -> None:
+ """Generic Data Record (GDR)
+
+ Contains information that does not conform to any other record type defined by the
+ STDF specification. Such records are intended to be written under the control of job
+ plans executing on the tester.
+
+ Note:
+ A test data file may contain any number of GDRs.
+
+ Args:
+ FLD_CNT (int): Count of data fields in record. Defaults to 0.
+ GEN_DATA (list[tuple[int, bytes]]): Data type code and data for one field. Defaults to None.
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.U_2.pack_into(obj.buffer, FLD_CNT)
+ # obj.V_n.pack_into(obj.buffer, GEN_DATA) # TODO: fix this
+
+ self.write_fields(50, 10, write_fields)
+
+ def DTR(
+ self,
+ TEXT_DAT: str = "",
+ ) -> None:
+ """Datalog Text Record (DTR)
+
+ Contains text information that is to be included in the datalog printout. DTRs may be
+ written under the control of a job plan: for example, to highlight unexpected test
+ results. They may also be generated by the tester executive software.
+
+ Note:
+ A test data file may contain any number of DTRs.
+
+ Args:
+ TEXT_DAT (str): ASCII text string. Defaults to "".
+ """
+
+ def write_fields(obj: "StdfPacker"):
+ obj.C_n.pack_into(obj.buffer, TEXT_DAT)
+
+ self.write_fields(50, 30, write_fields)
diff --git a/pystdf4/__init__.py b/pystdf4/__init__.py
index 7adf0f6..ea0e861 100644
--- a/pystdf4/__init__.py
+++ b/pystdf4/__init__.py
@@ -1,4 +1,4 @@
-from .IO.stfd4write import Stfd4Writer
+from .IO.stdf4writer import Stdf4Writer
-__version__ = "0.1.0"
-__all__ = ["Stfd4Writer"]
+__version__ = "0.1.1"
+__all__ = ["Stdf4Writer"]
diff --git a/tests/Core/test_data_base.py b/tests/Core/test_data_base.py
index 8bc03a0..17ea35a 100644
--- a/tests/Core/test_data_base.py
+++ b/tests/Core/test_data_base.py
@@ -31,7 +31,7 @@ class DummyField(FieldBase):
class TestImmediateField:
class DummyImmediate(ImmediateField):
@staticmethod
- def _normalize_value(value):
+ def _normalize_value(value, size=0):
return value
def setup_method(self):