Supported formats

"The same data" looks very different depending on which tool exported it. Rather than publish a list of formats we hope work, this page is generated from the test corpus: 84 files (19 centroid, 35 failure, 4 config, 20 profile, 6 bundle) that both implementations are run against on every change. One canonical board and dataset, re-expressed in each dialect, asserted to produce identical results.

Handled automatically

Structure

Comma, semicolon, tab and pipe delimiters, detected per file. UTF-8 byte order marks, CRLF endings, quoted fields containing the delimiter, and title or metadata rows above the header.

Numbers, dates and encodings

European decimal commas and thousands separators; dd-Mon-yyyy, yyyy/mm/dd, US m/d/y and two-digit years; Excel day serials; Windows-1252 files that would otherwise arrive as mojibake.

Excel workbooks

.xlsx is read directly. Shared strings and rich text are resolved, date-formatted cells are converted from day serials, and the sheet that was read — plus the ones that were not — is named, so data on another tab is visible rather than silently absent.

Column naming

Flexible aliases for designator, coordinate, date, quantity and assembly columns, including headings that carry their unit (Center-X(mil)) and defect columns matched by prefix (Defect Description, Failure Mode). Cells naming several parts (U7, U8) become separate records.

Refused, with a reason

Being permissive about the wrong things is worse than being strict. A placement export, a bill of materials, or a JSON document whose array is not defect records would each invent one "failure" per part, so each is rejected and told why. A recognized file that yields no records is an error, not a quiet success.

Placement / pick-and-place files

Example fileWhat it exercisesResult
centroid_comma.csv plain comma CSV parsed
centroid_tab.txt tab-delimited (Altium .txt) parsed
centroid_semicolon_eu.csv semicolon delimiter with comma decimals (European Excel) parsed
centroid_units_mm.csv units written into the value (Altium default) parsed
centroid_units_mil.csv mil units declared in the values parsed
centroid_bom.csv UTF-8 BOM (Excel save-as) parsed
centroid_crlf.csv CRLF line endings parsed
centroid_preamble.csv metadata/title lines above the header parsed
centroid_kicad.pos KiCad .pos — '#' header, whitespace separated parsed
centroid_quoted.csv quoted fields containing the delimiter parsed
centroid_aliases.csv alternative column names parsed
centroid_offset_origin.csv origin offset far from 0,0 — must not be mistaken for a huge board parsed
centroid_negative.csv negative coordinates parsed
centroid_duplicate.csv the same designator twice parsed
centroid_header_units.csv unit declared in the column heading, not the value (Altium mil) parsed
centroid_eagle.mnt headerless whitespace report (Eagle .mnt) parsed
centroid_blank_coords.csv rows with blank / non-numeric coordinates are counted, not silently dropped parsed
centroid_empty.csv empty file rejected, with a reason
centroid_no_columns.csv a BOM, not placement data rejected, with a reason

Failure data

Example fileWhat it exercisesResult
failures_ict_comma.csv ICT CSV, comma parsed
failures_ict_semicolon.csv ICT CSV, semicolon parsed
failures_ict_tab.txt ICT CSV, tab parsed
failures_ict_bom_crlf.csv ICT CSV, BOM + CRLF parsed
failures_ict_titlerow.csv title row above the header parsed
failures_repair.txt pipe-delimited repair log, US dates, free-text defects parsed
failures_qms.json QMS JSON parsed
failures_qms_bare_array.json bare top-level JSON array with alternative keys parsed
failures_generic_dates.csv generic CSV with dd-Mon-yyyy dates and free-text faults parsed
failures_multi_refdes.csv several designators in one cell parsed
failures_pipe_in_cell.csv one cell contains pipes — must not hijack the file into the repair-log parser parsed
failures_qty.csv thousands-separated quantity; an explicit quantity of 0 is not a failure parsed
failures_blank_refdes.csv rows with no designator are counted as unusable, not dropped in silence parsed
failures_qms_bom.json QMS JSON saved with a UTF-8 BOM parsed
failures_qms_numeric_code.json defect codes stored as JSON numbers parsed
failures_defect_description.csv defect column named 'Defect Description' rather than an exact alias parsed
failures_ampersand.csv '&'-joined designators, and a 'Failure Mode' defect column parsed
failures_padded_refdes.csv zero-padded designators are parsed verbatim, not silently renamed parsed
failures_multi_assembly.csv one export spanning two assemblies parsed
failures_ict_datetime.csv date-and-time cells in six shapes, one date-only, plus a station column parsed
failures_repair_time_station.txt repair log with separate TIME and STATION columns (one row has no time) parsed
failures_qms_datetime.json QMS timestamps with an offset, and a separate time field parsed
failures_qms_time_precedence.json time in the date cell wins over a time field; U+202F before PM; padded and numeric stations parsed
failures_impossible_dates.csv day-first and impossible dates (13th month, 30 February) become no date, not a rolled-over one parsed
failures_generic_time.csv generic CSV with separate date and time columns and a bench column parsed
failures_bom.csv a BOM would otherwise become one failure per part rejected, with a reason
failures_placement.json placement data under a generic 'data' key must not pass as a QMS export rejected, with a reason
failures_kicad_placement.csv KiCad placement CSV dropped into the failure intake rejected, with a reason
failures_empty_records.json recognized but contains nothing rejected, with a reason
failures_no_refdes.csv no designator column at all rejected, with a reason
failures_workbook.xlsx Excel workbook: shared strings, a second sheet, and dates stored as day serials parsed
profile_alt_repair.txt repair log in the alternate profile's vocabulary parsed
profile_alt_ict.csv quality-server codes from the alternate profile; unknown codes go to its fallback parsed
profile_alt_qms.json QMS values as key, label, code, hyphenated key and free text parsed
profile_alt_default_codes.csv the default profile's codes under the alternate profile parsed

Criticality configuration

Example fileWhat it exercisesResult
config_comma.csv config with mixed-case values parsed
config_semicolon.csv semicolon config with alias column names parsed
config_empty.csv empty config rejected, with a reason
config_no_refdes.csv config with no designator column rejected, with a reason

Customer profiles

What a defect means is configuration, not code: each manufacturer's failure types, entry-station hotkeys, repair vocabulary, defect codes, shift times and component classes live in one profile file that both implementations read. The corpus includes a second profile that differs from the default in every section, with failure files parsed under it, so nothing can quietly assume the default. Profiles that contradict themselves are refused with a reason.

Example fileWhat it exercisesResult
profile_alt.json a second profile differing from the default in every section parsed
profile_minimal.json only a taxonomy: rules and codes empty, shifts and classes from the default parsed
profile_bad_schema.json a JSON file that is not a profile rejected, with a reason
profile_bad_hotkey.json two failure types share a hotkey (case-insensitive) rejected, with a reason
profile_bad_rule.json a keyword rule naming the default's type, not this profile's rejected, with a reason
profile_bad_code.json a defect code naming a type the profile does not have rejected, with a reason
profile_bad_fallback.json fallback that is not one of the profile's types rejected, with a reason
profile_bad_timezone.json unknown IANA time zone rejected, with a reason
profile_bad_overlap.json a shift overlapping another (crossing midnight) rejected, with a reason
profile_bad_prefix.json one refdes prefix in two component classes rejected, with a reason
profile_bad_color.json a colour that is not #rrggbb rejected, with a reason
profile_bad_hotkey_newline.json a hotkey with a trailing newline is not a single key rejected, with a reason
profile_bad_fallback_type.json a fallback given as a list, not a type key rejected, with a reason
profile_bad_version.json version true is not version 1 rejected, with a reason
profile_bad_recent.json a recent period as long as the shortest classified window rejected, with a reason
profile_bad_share.json a share above 1 rejected, with a reason
profile_bad_threshold.json a day count given as true rejected, with a reason
profile_bad_window_days.json a window length past the calendar rejected, with a reason
profile_bad_dup_code.json two defect codes that are the same once normalized rejected, with a reason
profile_bom.json profile saved with a UTF-8 byte-order mark parsed

Data bundles

Records kept on a station — entered at the bench or brought in from another machine — export as one portable file with the kept boards and the customer profile. Importing one merges by record, so importing the same bundle twice adds nothing; rows that do not validate are counted and named, never dropped quietly or half-applied.

Example fileWhat it exercisesResult
bundle_records.json records for three board revisions, mixed date/datetime, nine unusable rows (one is demo data, two name days that do not exist) parsed
bundle_with_profile.json a bundle carrying its customer profile; records keep that profile's types parsed
bundle_loose.json version written as 1.0 and boards as null — read the same by both pipelines parsed
bundle_newer.json a bundle from a newer version is refused, not half-read rejected, with a reason
bundle_not_a_bundle.json a board package is not a data bundle rejected, with a reason
bundle_bad_profile.json a bundle whose profile contradicts itself rejected, with a reason

Something not on this list?

Reading a new export dialect is a small, contained change — the adapters are deliberately isolated from everything downstream. Send a sample (with anything sensitive removed) to info@engsecsolutions.com and it will be added to this corpus, which means it stays working.