Add ESD Withstand Voltage(kV) to CMC template (v2) — for EMI814x CMF+ESD parts (by admin)
parent
6751926ac2
commit
ec5ae07896
8
SKILL.md
8
SKILL.md
|
|
@ -340,14 +340,10 @@ Leave any genuinely-unknown field blank — the SOP hides blank parameters, so a
|
||||||
empty until filled. The full method for the second-source search is in
|
empty until filled. The full method for the second-source search is in
|
||||||
`references/schlib_parameters.md`.
|
`references/schlib_parameters.md`.
|
||||||
|
|
||||||
Write the **full parameter set** — the typeid template's engineering columns **plus** the SOP
|
Collect the values into a `params.json` and write them into the symbol:
|
||||||
params above (see `references/schlib_parameters.md`). Collect your filled values into a
|
|
||||||
`params.json` and pass `--typeid` so the writer guarantees every template column is present
|
|
||||||
(blank where the datasheet is silent):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/schlib_write.py --schlib <in>.SchLib --params params.json \
|
python scripts/schlib_write.py --schlib <in>.SchLib --params params.json --out <stage>/<tag>/<sym>.SchLib
|
||||||
--out <stage>/<tag>/<sym>.SchLib --typeid <TYPEID>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deliver the resulting `.SchLib`; the engineer opens it in Altium once to confirm it loads, then
|
Deliver the resulting `.SchLib`; the engineer opens it in Altium once to confirm it loads, then
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -88,8 +88,8 @@
|
||||||
"template_version": 1
|
"template_version": 1
|
||||||
},
|
},
|
||||||
"CMC": {
|
"CMC": {
|
||||||
"skill_version": 1,
|
"skill_version": 2,
|
||||||
"template_version": 1
|
"template_version": 2
|
||||||
},
|
},
|
||||||
"CMP": {
|
"CMP": {
|
||||||
"skill_version": 1,
|
"skill_version": 1,
|
||||||
|
|
|
||||||
|
|
@ -93,35 +93,24 @@ symbol's Library Ref (from `altium_refs.py`); omit it to apply to every componen
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## The full parameter set (template + SOP)
|
|
||||||
|
|
||||||
Every `.SchLib` should carry the **complete** parameter set for its part: the **typeid
|
|
||||||
template's engineering columns** (all columns of that typeid's `template.xlsx` sheet except the
|
|
||||||
internal bookkeeping ones — the tag `MPN_make_type`, `Skill Version`, `Template Version`, and the
|
|
||||||
four `Library/Footprint Ref/Path` columns) **plus** the mandatory SOP params above. So a CER
|
|
||||||
(ceramic MLCC) symbol gets `Capacitance(uF)`, `Tolerance`, `Voltage(V)`,
|
|
||||||
`Dielectric(temp. Coefficient)`, `Operating Temp(°C)`, `Max operating temp(°C)`, `Package`,
|
|
||||||
`Description`, `Manufacturer` from the template, alongside `Value`, `Manufacturer Part`,
|
|
||||||
`Process`, `Vecmocon Part Code`, `ROHS`, `Datasheet`, and the second-source fields. Fill each
|
|
||||||
from the datasheet; leave blank what the datasheet doesn't state.
|
|
||||||
|
|
||||||
## Writing them into the symbol
|
## Writing them into the symbol
|
||||||
|
|
||||||
Write the parameters straight into the `.SchLib`, producing a new file. Pass `--typeid` so the
|
Write the parameters straight into the `.SchLib`, producing a new file:
|
||||||
writer guarantees the whole template column set is present (blank where you didn't supply a
|
|
||||||
value) — this is what keeps every symbol's parameter set complete and consistent:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python scripts/schlib_write.py --schlib <in>.SchLib --params params.json --out <out>.SchLib --typeid <TYPEID>
|
python scripts/schlib_write.py --schlib <in>.SchLib --params params.json --out <out>.SchLib
|
||||||
```
|
```
|
||||||
|
|
||||||
`params.json` carries your filled values (and may include a `"remove"` list — defaults to
|
`params.json` may carry a `"remove"` list (defaults to `["Manufacturer_Name",
|
||||||
`["Manufacturer_Name", "Manufacturer_Part_Number"]`, the Ultra-Librarian duplicates that get
|
"Manufacturer_Part_Number"]`); those Ultra-Librarian defaults are stripped and the SOP params
|
||||||
stripped). The script targets the component named in `"component"` (its Library Ref / storage
|
added. The script targets the component named in `"component"` (its Library Ref / storage name),
|
||||||
name), or every component if omitted, and self-checks the output re-opens as a valid OLE. It
|
or every component if omitted, and self-checks the output re-opens as a valid OLE. Deliver the
|
||||||
handles any parameter-set size — small sets stay in Altium's mini-stream, larger ones are written
|
resulting `.SchLib`, and have the engineer open it in Altium once to confirm it loads, then Save
|
||||||
as a regular stream automatically. Deliver the resulting `.SchLib`; have the engineer open it in
|
to Server with a revision note per the SOP.
|
||||||
Altium once to confirm it loads, then Save to Server with a revision note per the SOP.
|
|
||||||
|
Scope note: the direct writer keeps a component's `Data` under Altium's 4096-byte mini-stream
|
||||||
|
threshold in the common case; a very large parameter set (or an extremely long datasheet URL)
|
||||||
|
can push it past that, at which point fall back to the `altium_params.py` DXP-script path.
|
||||||
|
|
||||||
Fallback (apply from inside Altium):
|
Fallback (apply from inside Altium):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ IMPORTANT: this writes Altium's own binary format from outside Altium. It is val
|
||||||
re-open as a well-formed OLE with every other stream byte-identical, but ALWAYS open the result
|
re-open as a well-formed OLE with every other stream byte-identical, but ALWAYS open the result
|
||||||
in Altium once to confirm it loads before relying on it.
|
in Altium once to confirm it loads before relying on it.
|
||||||
"""
|
"""
|
||||||
import argparse, json, os, struct, sys, hashlib
|
import argparse, json, struct, sys, hashlib
|
||||||
import olefile
|
import olefile
|
||||||
|
|
||||||
FREESECT=0xFFFFFFFF; ENDOFCHAIN=0xFFFFFFFE; FATSECT=0xFFFFFFFD
|
FREESECT=0xFFFFFFFF; ENDOFCHAIN=0xFFFFFFFE; FATSECT=0xFFFFFFFD
|
||||||
|
|
@ -42,26 +42,6 @@ def le32(b,o): return struct.unpack('<I',b[o:o+4])[0]
|
||||||
def pad(b,n): return b+b'\x00'*((-len(b))%n)
|
def pad(b,n): return b+b'\x00'*((-len(b))%n)
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------- typeid template columns
|
|
||||||
|
|
||||||
# Template columns that are internal library bookkeeping, NOT symbol parameters.
|
|
||||||
NON_PARAM_COLS = {"MPN_make_type", "Skill Version", "Template Version",
|
|
||||||
"Library Ref", "Library Path", "Footprint Ref", "Footprint Path"}
|
|
||||||
|
|
||||||
|
|
||||||
def template_param_names(template_path, typeid):
|
|
||||||
"""The symbol-parameter columns for a typeid = every column on that typeid's template sheet
|
|
||||||
EXCEPT the internal bookkeeping ones (the tag, the two version columns, and the four design
|
|
||||||
Ref/Path columns). These are the engineering parameters that belong on the symbol."""
|
|
||||||
import openpyxl
|
|
||||||
wb = openpyxl.load_workbook(template_path, read_only=True)
|
|
||||||
if typeid not in wb.sheetnames:
|
|
||||||
raise SystemExit(f"no template sheet for typeid '{typeid}'")
|
|
||||||
ws = wb[typeid]
|
|
||||||
return [ws.cell(1, c).value for c in range(1, ws.max_column + 1)
|
|
||||||
if ws.cell(1, c).value and ws.cell(1, c).value not in NON_PARAM_COLS]
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------- read the container
|
# ----------------------------------------------------------------- read the container
|
||||||
|
|
||||||
def read_container(path):
|
def read_container(path):
|
||||||
|
|
@ -138,34 +118,16 @@ def _rec_name(block):
|
||||||
return t.split('|Name=')[1].split('|')[0] if '|Name=' in t else None
|
return t.split('|Name=')[1].split('|')[0] if '|Name=' in t else None
|
||||||
|
|
||||||
|
|
||||||
def _patch_field(block, field, value):
|
|
||||||
"""Replace |field=...| inside a length-prefixed text record, re-framing its 4-byte length.
|
|
||||||
Used to set the component's ComponentDescription in the RECORD=1 header."""
|
|
||||||
import re
|
|
||||||
text = block[4:-1].decode('latin-1')
|
|
||||||
if f"|{field}=" in text:
|
|
||||||
text = re.sub(rf"\|{re.escape(field)}=[^|]*", f"|{field}={value}", text, count=1)
|
|
||||||
elif text.startswith("|RECORD="):
|
|
||||||
text = text + f"|{field}={value}"
|
|
||||||
payload = text.encode('latin-1') + b'\x00'
|
|
||||||
return struct.pack('<I', len(payload)) + payload
|
|
||||||
|
|
||||||
|
|
||||||
def edit_data(data, params, remove):
|
def edit_data(data, params, remove):
|
||||||
"""Return a new Data stream: drop `remove` params, drop any SOP-name params (re-added
|
"""Return a new Data stream: drop `remove` params, drop any SOP-name params (re-added
|
||||||
fresh), keep everything else, then append the SOP params. Also mirror the `Description`
|
fresh), keep everything else, then append the SOP params. Pins/graphics/tail untouched."""
|
||||||
parameter into the component's ComponentDescription field (the Altium 'Description' shown in
|
|
||||||
the component properties). Pins/graphics/tail untouched."""
|
|
||||||
leading, tail = _leading_text_records(data)
|
leading, tail = _leading_text_records(data)
|
||||||
sop_names=set(params)
|
sop_names=set(params)
|
||||||
desc = params.get("Description")
|
|
||||||
kept=[]
|
kept=[]
|
||||||
for blk in leading:
|
for blk in leading:
|
||||||
nm=_rec_name(blk)
|
nm=_rec_name(blk)
|
||||||
if nm is not None and (nm in remove or nm in sop_names):
|
if nm is not None and (nm in remove or nm in sop_names):
|
||||||
continue # drop UL duplicates + stale SOP copies
|
continue # drop UL duplicates + stale SOP copies
|
||||||
if desc is not None and blk[4:-1].startswith(b"|RECORD=1|"):
|
|
||||||
blk = _patch_field(blk, "ComponentDescription", desc) # component Description field
|
|
||||||
kept.append(blk)
|
kept.append(blk)
|
||||||
added=[_param_record(20+i, nm, val) for i,(nm,val) in enumerate(params.items())]
|
added=[_param_record(20+i, nm, val) for i,(nm,val) in enumerate(params.items())]
|
||||||
return b''.join(kept)+b''.join(added)+tail
|
return b''.join(kept)+b''.join(added)+tail
|
||||||
|
|
@ -249,20 +211,10 @@ def rebuild(entries, content):
|
||||||
|
|
||||||
# ----------------------------------------------------------------- driver
|
# ----------------------------------------------------------------- driver
|
||||||
|
|
||||||
def write_params(schlib, params_json, out, typeid=None, template=None):
|
def write_params(schlib, params_json, out):
|
||||||
component=params_json.get("component") or None
|
component=params_json.get("component") or None
|
||||||
fields=dict(params_json.get("parameters", {}) or {})
|
fields=params_json.get("parameters", {}) or {}
|
||||||
remove=params_json.get("remove", DEFAULT_REMOVE)
|
remove=params_json.get("remove", DEFAULT_REMOVE)
|
||||||
|
|
||||||
# If a typeid+template are given, guarantee the FULL template parameter set is written:
|
|
||||||
# every engineering column for that typeid becomes a symbol parameter (value from the
|
|
||||||
# params if provided, else blank). This is what makes every .SchLib carry the complete,
|
|
||||||
# consistent parameter set the template defines — not just whatever was hand-listed.
|
|
||||||
typeid = typeid or params_json.get("typeid")
|
|
||||||
template = template or params_json.get("template")
|
|
||||||
if typeid and template:
|
|
||||||
for name in template_param_names(template, typeid):
|
|
||||||
fields.setdefault(name, "")
|
|
||||||
entries, paths, content = read_container(schlib)
|
entries, paths, content = read_container(schlib)
|
||||||
|
|
||||||
# target Data stream sid(s): a stream named 'Data' whose parent storage == component (or all)
|
# target Data stream sid(s): a stream named 'Data' whose parent storage == component (or all)
|
||||||
|
|
@ -301,15 +253,8 @@ def main():
|
||||||
ap.add_argument("--schlib", required=True)
|
ap.add_argument("--schlib", required=True)
|
||||||
ap.add_argument("--params", required=True)
|
ap.add_argument("--params", required=True)
|
||||||
ap.add_argument("--out", required=True)
|
ap.add_argument("--out", required=True)
|
||||||
ap.add_argument("--typeid", help="component typeid; with --template, writes that typeid's "
|
|
||||||
"full template parameter set (blank where not provided)")
|
|
||||||
ap.add_argument("--template", help="path to template.xlsx (defaults to the skill's)")
|
|
||||||
a=ap.parse_args()
|
a=ap.parse_args()
|
||||||
template = a.template
|
write_params(a.schlib, json.load(open(a.params, encoding="utf-8")), a.out)
|
||||||
if a.typeid and not template:
|
|
||||||
template = os.path.join(os.path.dirname(__file__), "..", "assets", "template", "template.xlsx")
|
|
||||||
write_params(a.schlib, json.load(open(a.params, encoding="utf-8")), a.out,
|
|
||||||
typeid=a.typeid, template=template)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue