190 lines
6.4 KiB
Python
190 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate an Altium DelphiScript that stamps the mandatory parameters (SOP §5) onto a
|
|
schematic-library symbol, from a params.json the skill builds off the datasheet.
|
|
|
|
Why a script instead of editing the .SchLib directly: a .SchLib is an OLE compound-binary that
|
|
we can read but cannot safely grow from outside Altium (adding parameter records means
|
|
rewriting Altium's own format). So the skill produces the parameter *values* from the datasheet
|
|
and emits a small script the engineer runs inside Altium (DXP -> Run Script -> ApplyParameters),
|
|
which adds/updates exactly these parameters on the component and sets the Comment to the MPN.
|
|
See references/schlib_parameters.md for the parameter set and each value's source.
|
|
|
|
params.json:
|
|
{
|
|
"component": "CC0402FRNPO9BN120", # Library Ref of the target symbol; omit -> all comps
|
|
"comment": "CC0402FRNPO9BN120", # sets the Comment field (usually the MPN)
|
|
"parameters": {"Value": "12pF_0402", "Manufacturer": "YAGEO", ...}
|
|
}
|
|
|
|
Usage:
|
|
python altium_params.py script --params params.json --out apply_params.pas
|
|
python altium_params.py script --params params.json # prints to stdout
|
|
|
|
The generated .pas is plain text; validate its first run on a new Altium version, since the
|
|
scripting API can differ slightly between releases.
|
|
"""
|
|
import argparse, json, sys
|
|
|
|
|
|
def _q(s):
|
|
"""Quote a value as a DelphiScript single-quoted string literal (doubling embedded quotes).
|
|
None/blank -> ''. Non-strings are stringified."""
|
|
if s is None:
|
|
s = ""
|
|
return "'" + str(s).replace("'", "''") + "'"
|
|
|
|
|
|
PROLOGUE = """{ Auto-generated by library-manager (altium_params.py).
|
|
Stamps the SOP mandatory parameters onto a schematic-library symbol.
|
|
HOW TO RUN: open the target .SchLib in Altium, then DXP -> Run Script -> pick this file
|
|
-> run 'ApplyParameters'. It adds each parameter if missing, updates it if present, sets the
|
|
Comment field, hides blank-valued parameters, and leaves the library open for you to review
|
|
and Save to Server (with a revision note, per the SOP). }
|
|
|
|
Var
|
|
Lib : ISch_Lib;
|
|
LibIter : ISch_Iterator;
|
|
Comp : ISch_Component;
|
|
TargetRef: String;
|
|
CommentText : String;
|
|
|
|
Procedure SetParam(AComp : ISch_Component; AName : String; AValue : String);
|
|
Var
|
|
PIter : ISch_Iterator;
|
|
P : ISch_Parameter;
|
|
Found : ISch_Parameter;
|
|
Begin
|
|
Found := Nil;
|
|
PIter := AComp.SchIterator_Create;
|
|
PIter.AddFilter_ObjectSet(MkSet(eParameter));
|
|
Try
|
|
P := PIter.FirstSchObject;
|
|
While P <> Nil Do
|
|
Begin
|
|
If P.Name = AName Then Found := P;
|
|
P := PIter.NextSchObject;
|
|
End;
|
|
Finally
|
|
AComp.SchIterator_Destroy(PIter);
|
|
End;
|
|
|
|
If Found <> Nil Then
|
|
Begin
|
|
Found.Text := AValue;
|
|
Found.IsHidden := (AValue = '');
|
|
End
|
|
Else
|
|
Begin
|
|
Found := SchServer.SchObjectFactory(eParameter, eCreate_GlobalCopy);
|
|
Found.Name := AName;
|
|
Found.Text := AValue;
|
|
Found.IsHidden := (AValue = '');
|
|
AComp.AddSchObject(Found);
|
|
End;
|
|
End;
|
|
|
|
Procedure ApplyToComponent(AComp : ISch_Component);
|
|
Begin
|
|
SchServer.RobotManager.SendMessage(AComp.I_ObjectAddress, c_BroadCast,
|
|
SCHM_BeginModify, c_NoEventData);
|
|
Try
|
|
If CommentText <> '' Then AComp.Comment.Text := CommentText;
|
|
"""
|
|
|
|
EPILOGUE = """ Finally
|
|
SchServer.RobotManager.SendMessage(AComp.I_ObjectAddress, c_BroadCast,
|
|
SCHM_EndModify, c_NoEventData);
|
|
End;
|
|
End;
|
|
|
|
Procedure ApplyParameters;
|
|
Begin
|
|
If SchServer = Nil Then
|
|
Begin
|
|
ShowMessage('Schematic server not available.');
|
|
Exit;
|
|
End;
|
|
Lib := SchServer.GetCurrentSchDocument;
|
|
If Lib = Nil Then
|
|
Begin
|
|
ShowMessage('Open the target .SchLib first.');
|
|
Exit;
|
|
End;
|
|
If Lib.ObjectID <> eSchLib Then
|
|
Begin
|
|
ShowMessage('The active document is not a .SchLib.');
|
|
Exit;
|
|
End;
|
|
|
|
LibIter := Lib.SchLibIterator_Create;
|
|
LibIter.AddFilter_ObjectSet(MkSet(eSchComponent));
|
|
Try
|
|
Comp := LibIter.FirstSchObject;
|
|
While Comp <> Nil Do
|
|
Begin
|
|
If (TargetRef = '') Or (Comp.LibReference = TargetRef) Then
|
|
ApplyToComponent(Comp);
|
|
Comp := LibIter.NextSchObject;
|
|
End;
|
|
Finally
|
|
Lib.SchLibIterator_Destroy(LibIter);
|
|
End;
|
|
|
|
Lib.GraphicallyInvalidate;
|
|
ShowMessage('Parameters applied. Review the component, then Save to Server.');
|
|
End;
|
|
"""
|
|
|
|
|
|
def generate(params):
|
|
comp = params.get("component", "") or ""
|
|
comment = params.get("comment", "") or ""
|
|
fields = params.get("parameters", {}) or {}
|
|
|
|
setparam_calls = "\n".join(
|
|
f" SetParam(AComp, {_q(name)}, {_q(value)});" for name, value in fields.items()
|
|
)
|
|
|
|
# TargetRef / CommentText are globals set by InitTargets, called first in ApplyParameters.
|
|
init_block = (
|
|
"Procedure InitTargets;\n"
|
|
"Begin\n"
|
|
f" TargetRef := {_q(comp)};\n"
|
|
f" CommentText := {_q(comment)};\n"
|
|
"End;\n\n"
|
|
)
|
|
|
|
body = PROLOGUE + setparam_calls + "\n" + EPILOGUE
|
|
# Ensure InitTargets is called first inside ApplyParameters.
|
|
body = body.replace("Procedure ApplyParameters;\nBegin\n",
|
|
"Procedure ApplyParameters;\nBegin\n InitTargets;\n")
|
|
# Place InitTargets AFTER the global Var block (Pascal: declarations before use), i.e. just
|
|
# before the first procedure. The Var block lives at the top of PROLOGUE.
|
|
return body.replace("Procedure SetParam(AComp",
|
|
init_block + "Procedure SetParam(AComp", 1)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
s = sub.add_parser("script", help="emit the Altium DelphiScript from a params.json")
|
|
s.add_argument("--params", required=True)
|
|
s.add_argument("--out", help="write the .pas here (default: stdout)")
|
|
a = ap.parse_args()
|
|
|
|
params = json.load(open(a.params, encoding="utf-8"))
|
|
pas = generate(params)
|
|
if a.out:
|
|
with open(a.out, "w", encoding="utf-8") as f:
|
|
f.write(pas)
|
|
n = len(params.get("parameters", {}))
|
|
print(f"wrote {a.out} ({n} parameters"
|
|
+ (f", component '{params['component']}'" if params.get("component") else ", all components")
|
|
+ ")")
|
|
else:
|
|
sys.stdout.write(pas)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|