app
adom-desktop-altium-bridge
Public Unreviewedby barrett-land
Adom Desktop bridge for Altium Designer — add LCSC/JLCPCB-tagged parts to file-based libraries (symbols, footprints with 3D, value-only display) by driving Altium via DelphiScript.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
// AdomAltium.pas — Adom Altium bridge in-host dispatcher (original Adom code).
//
// Invoked by the bridge via:
// X2.EXE -RScriptingSystem:RunScript(ProjectName=...AdomAltium.PrjScr|ProcName=AdomAltium>Run)
// Reads C:\Users\Public\AdomAltium\request.txt (simple key=value lines)
// Writes C:\Users\Public\AdomAltium\response.json (hand-built JSON)
//
// Request keys (per command):
// command = ping | add_lcsc_param | read_params
// add_lcsc_param: schlib, component, lcsc, mpn, manufacturer, value, description, package, datasheet
// read_params: schlib, component
const
EX_DIR = 'C:\Users\Public\AdomAltium\';
var
Req : TStringList;
LastServerDoc : IServerDocument; // set by OpenSchLibDoc, used to save
GStage : String; // last stage reached, for error pinpointing
GReqId : String; // id of the request being served (echoed in response)
{ ---- JSON string escaping (minimal, original) ---- }
function JStr(const S : String) : String;
var i : Integer; c : Char; o : String;
begin
o := '';
for i := 1 to Length(S) do
begin
c := S[i];
if (c = '\') or (c = '"') then o := o + '\' + c
else if c = #9 then o := o + '\t'
else if c = #10 then o := o + '\n'
else if c = #13 then o := o + '\r'
else o := o + c;
end;
Result := '"' + o + '"';
end;
function BoolStr(b : Boolean) : String;
begin
if b then Result := 'true' else Result := 'false';
end;
procedure WriteResp(const JsonBody : String);
var R : TStringList;
begin
if not DirectoryExists(EX_DIR) then ForceDirectories(EX_DIR);
R := TStringList.Create;
try
R.Text := JsonBody;
R.SaveToFile(EX_DIR + 'response.json');
finally
R.Free;
end;
end;
procedure RespOk(const ExtraPairs : String);
begin
WriteResp('{ "ok": true, "id": ' + JStr(GReqId) + ExtraPairs + ' }');
end;
procedure RespErr(const Msg : String);
begin
WriteResp('{ "ok": false, "id": ' + JStr(GReqId) + ', "error": ' + JStr(Msg) + ' }');
end;
{ ---- open a SchLib; returns ISch_Lib and stashes the server doc for saving ---- }
function OpenSchLibDoc(const Path : String) : ISch_Lib;
begin
Result := nil;
LastServerDoc := nil;
if SchServer = nil then Exit;
LastServerDoc := Client.OpenDocument('SCHLIB', Path);
if LastServerDoc <> nil then
Client.ShowDocument(LastServerDoc);
Result := SchServer.GetSchDocumentByPath(Path);
end;
procedure SaveCurrentDoc;
begin
if LastServerDoc <> nil then
LastServerDoc.DoFileSave('SCHLIB');
end;
{ ---- find a component in a SchLib by LibReference (empty = first) ---- }
function FindComponent(SchLib : ISch_Lib; const NameRef : String) : ISch_Component;
var It : ISch_Iterator; Cmp : ISch_Component;
begin
Result := nil;
It := SchLib.SchLibIterator_Create;
It.AddFilter_ObjectSet(MkSet(eSchComponent));
try
Cmp := It.FirstSchObject;
while Cmp <> nil do
begin
if (NameRef = '') or (Cmp.LibReference = NameRef) then
begin
Result := Cmp;
Break;
end;
Cmp := It.NextSchObject;
end;
finally
SchLib.SchIterator_Destroy(It);
end;
end;
{ ---- set or replace a parameter on a component ---- }
procedure SetParam(Cmp : ISch_Component; const PName, PVal : String);
var It : ISch_Iterator; P, Existing : ISch_Parameter;
begin
Existing := nil;
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eParameter));
try
P := It.FirstSchObject;
while P <> nil do
begin
if P.Name = PName then begin Existing := P; Break; end;
P := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
if Existing <> nil then
Existing.Text := PVal
else
begin
P := SchServer.SchObjectFactory(eParameter, eCreate_GlobalCopy);
P.Name := PName;
P.Text := PVal;
P.IsHidden := False;
Cmp.AddSchObject(P);
end;
end;
procedure MaybeSet(Cmp : ISch_Component; const PName, Key : String);
var V : String;
begin
V := Req.Values[Key];
if V <> '' then SetParam(Cmp, PName, V);
end;
{ ---- command: add LCSC + identity params to a component ---- }
procedure CmdAddLcscParam;
var SchLibPath, CompRef : String; SchLib : ISch_Lib; Cmp : ISch_Component;
begin
SchLibPath := Req.Values['schlib'];
CompRef := Req.Values['component'];
if SchLibPath = '' then begin RespErr('missing schlib'); Exit; end;
SchLib := OpenSchLibDoc(SchLibPath);
if SchLib = nil then begin RespErr('could not open schlib: ' + SchLibPath); Exit; end;
Cmp := FindComponent(SchLib, CompRef);
if Cmp = nil then begin RespErr('component not found: ' + CompRef); Exit; end;
MaybeSet(Cmp, 'LCSC Part #', 'lcsc');
MaybeSet(Cmp, 'Manufacturer Part Number', 'mpn');
MaybeSet(Cmp, 'Manufacturer', 'manufacturer');
MaybeSet(Cmp, 'Value', 'value');
MaybeSet(Cmp, 'Description', 'description');
MaybeSet(Cmp, 'Package', 'package');
MaybeSet(Cmp, 'Datasheet', 'datasheet');
SchLib.GraphicallyInvalidate;
SaveCurrentDoc;
RespOk(', "component": ' + JStr(Cmp.LibReference) +
', "lcsc": ' + JStr(Req.Values['lcsc']));
end;
{ ---- command: read back a component's parameters (verification) ---- }
procedure CmdReadParams;
var SchLibPath, CompRef, Acc : String; SchLib : ISch_Lib; Cmp : ISch_Component;
It : ISch_Iterator; P : ISch_Parameter; First : Boolean;
begin
SchLibPath := Req.Values['schlib'];
CompRef := Req.Values['component'];
SchLib := OpenSchLibDoc(SchLibPath);
if SchLib = nil then begin RespErr('could not open schlib: ' + SchLibPath); Exit; end;
Cmp := FindComponent(SchLib, CompRef);
if Cmp = nil then begin RespErr('component not found: ' + CompRef); Exit; end;
Acc := ''; First := True;
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eParameter));
try
P := It.FirstSchObject;
while P <> nil do
begin
if not First then Acc := Acc + ', ';
Acc := Acc + JStr(P.Name) + ': ' + JStr(P.Text);
First := False;
P := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
RespOk(', "component": ' + JStr(Cmp.LibReference) + ', "params": { ' + Acc + ' }');
end;
{ ---- read back all params of a component into a JSON object body ---- }
function ParamsJSON(Cmp : ISch_Component) : String;
var It : ISch_Iterator; P : ISch_Parameter; Acc : String; First : Boolean;
begin
Acc := ''; First := True;
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eParameter));
try
P := It.FirstSchObject;
while P <> nil do
begin
if not First then Acc := Acc + ', ';
Acc := Acc + JStr(P.Name) + ': ' + JStr(P.Text);
First := False;
P := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
Result := '{ ' + Acc + ' }';
end;
{ ---- command: create a new SchLib + component, attach LCSC/identity params,
read them back, and best-effort save. Proves the keystone end-to-end
with no external symbol source. Stage-tracked for error pinpointing. ---- }
procedure CmdCreateAndAttach;
var
CompName, Path : String;
SD : IServerDocument;
SchLib : ISch_Lib;
Cmp : ISch_Component;
SavedOK : Boolean;
begin
CompName := Req.Values['component']; if CompName = '' then CompName := 'TESTPART';
Path := Req.Values['schlib'];
GStage := 'SchServer';
if SchServer = nil then begin RespErr('SchServer is nil'); Exit; end;
GStage := 'CreateNewDocumentFromDocumentKind(SchLib)';
SD := CreateNewDocumentFromDocumentKind('SchLib');
GStage := 'ShowDocument';
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
GStage := 'GetCurrentSchDocument';
SchLib := SchServer.GetCurrentSchDocument;
if SchLib = nil then begin RespErr('GetCurrentSchDocument nil at ' + GStage); Exit; end;
GStage := 'SchObjectFactory(eSchComponent)';
Cmp := SchServer.SchObjectFactory(eSchComponent, eCreate_Default);
if Cmp = nil then begin RespErr('component factory nil'); Exit; end;
GStage := 'set component props';
Cmp.CurrentPartID := 1;
Cmp.DisplayModeCount := 1;
Cmp.LibReference := CompName;
Cmp.Designator.Text := 'U?';
Cmp.Comment.Text := CompName;
GStage := 'AddSchComponent';
SchLib.AddSchComponent(Cmp);
GStage := 'register primitive';
SchServer.RobotManager.SendMessage(SchLib.I_ObjectAddress, c_BroadCast,
SCHM_PrimitiveRegistration, Cmp.I_ObjectAddress);
GStage := 'set params';
MaybeSet(Cmp, 'LCSC Part #', 'lcsc');
MaybeSet(Cmp, 'Manufacturer Part Number', 'mpn');
MaybeSet(Cmp, 'Manufacturer', 'manufacturer');
MaybeSet(Cmp, 'Value', 'value');
MaybeSet(Cmp, 'Description', 'description');
GStage := 'invalidate';
SchLib.GraphicallyInvalidate;
GStage := 'save';
SavedOK := False;
if (Path <> '') and (LastServerDoc <> nil) then
begin
LastServerDoc.SetFileName(Path);
SavedOK := LastServerDoc.DoFileSave('');
end;
GStage := 'done';
if SavedOK then
RespOk(', "component": ' + JStr(CompName) +
', "saved": true, "saved_to": ' + JStr(Path) +
', "params": ' + ParamsJSON(Cmp))
else
RespOk(', "component": ' + JStr(CompName) +
', "saved": false, "params": ' + ParamsJSON(Cmp));
end;
{ ---- command: create a proper 2-pin resistor symbol with body, params, and a
footprint implementation link, then save the SchLib ---- }
procedure CmdCreateResistor;
var
SD : IServerDocument;
Lib : ISch_Lib;
Cmp : ISch_Component;
R : ISch_Rectangle;
P1, P2 : ISch_Pin;
Impl : ISch_Implementation;
SchPath, FpName, FpLib, SavedStr : String;
SavedOK : Boolean;
begin
SchPath := Req.Values['schlib'];
FpName := Req.Values['footprint']; if FpName = '' then FpName := 'R0603';
FpLib := Req.Values['footprint_lib']; if FpLib = '' then FpLib := 'Resistors.PcbLib';
GStage := 'create SchLib doc';
SD := CreateNewDocumentFromDocumentKind('SchLib');
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
Lib := SchServer.GetCurrentSchDocument;
if Lib = nil then begin RespErr('no SchLib'); Exit; end;
GStage := 'create component';
Cmp := SchServer.SchObjectFactory(eSchComponent, eCreate_Default);
if Cmp = nil then begin RespErr('component factory nil'); Exit; end;
Cmp.CurrentPartID := 1;
Cmp.DisplayMode := 0;
Cmp.LibReference := Req.Values['component'];
if Cmp.LibReference = '' then Cmp.LibReference := 'R';
GStage := 'rectangle body';
R := SchServer.SchObjectFactory(eRectangle, eCreate_Default);
R.LineWidth := eSmall;
R.Location := Point(MilsToCoord(0), MilsToCoord(-50));
R.Corner := Point(MilsToCoord(300), MilsToCoord(50));
R.IsSolid := False;
R.OwnerPartId := Cmp.CurrentPartID;
R.OwnerPartDisplayMode := Cmp.DisplayMode;
GStage := 'pin 1';
P1 := SchServer.SchObjectFactory(ePin, eCreate_Default);
P1.Location := Point(MilsToCoord(-100), MilsToCoord(0));
P1.Orientation := eRotate0;
P1.Designator := '1';
P1.Name := '1';
P1.OwnerPartId := Cmp.CurrentPartID;
P1.OwnerPartDisplayMode := Cmp.DisplayMode;
GStage := 'pin 2';
P2 := SchServer.SchObjectFactory(ePin, eCreate_Default);
P2.Location := Point(MilsToCoord(400), MilsToCoord(0));
P2.Orientation := eRotate180;
P2.Designator := '2';
P2.Name := '2';
P2.OwnerPartId := Cmp.CurrentPartID;
P2.OwnerPartDisplayMode := Cmp.DisplayMode;
GStage := 'add primitives';
Cmp.AddSchObject(P1);
Cmp.AddSchObject(P2);
Cmp.AddSchObject(R);
GStage := 'designator + params';
Cmp.Designator.Text := 'R?';
if Req.Values['value'] <> '' then Cmp.Comment.Text := Req.Values['value'];
MaybeSet(Cmp, 'LCSC Part #', 'lcsc');
MaybeSet(Cmp, 'Manufacturer Part Number', 'mpn');
MaybeSet(Cmp, 'Manufacturer', 'manufacturer');
MaybeSet(Cmp, 'Value', 'value');
MaybeSet(Cmp, 'Description', 'description');
GStage := 'footprint implementation';
Impl := Cmp.AddSchImplementation;
Impl.ClearAllDatafileLinks;
Impl.ModelName := FpName;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(FpName, '', 'PCBLIB');
Impl.IsCurrent := True;
GStage := 'AddSchComponent';
Lib.AddSchComponent(Cmp);
SchServer.RobotManager.SendMessage(Lib.I_ObjectAddress, c_BroadCast,
SCHM_PrimitiveRegistration, Cmp.I_ObjectAddress);
GStage := 'save';
SavedOK := False;
if (SchPath <> '') and (LastServerDoc <> nil) then
begin
LastServerDoc.SetFileName(SchPath);
SavedOK := LastServerDoc.DoFileSave('');
end;
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
GStage := 'done';
RespOk(', "component": ' + JStr(Cmp.LibReference) +
', "saved": ' + SavedStr +
', "saved_to": ' + JStr(SchPath) +
', "footprint_link": ' + JStr(FpLib + ':' + FpName) +
', "params": ' + ParamsJSON(Cmp));
end;
{ ---- command: create a 2-pad SMD 0603 footprint in a PcbLib, then save ---- }
procedure CmdCreateFootprint;
var
SD : IServerDocument;
PcbLib : IPCB_Library;
FpComp : IPCB_LibComponent;
Pad : IPCB_Pad;
PcbPath, FpName, SavedStr : String;
SavedOK : Boolean;
begin
PcbPath := Req.Values['pcblib'];
FpName := Req.Values['footprint']; if FpName = '' then FpName := 'R0603';
GStage := 'create PcbLib doc';
SD := CreateNewDocumentFromDocumentKind('PcbLib');
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
GStage := 'GetCurrentPCBLibrary';
if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;
PcbLib := PCBServer.GetCurrentPCBLibrary;
if PcbLib = nil then begin RespErr('no current PcbLib'); Exit; end;
GStage := 'CreatePCBLibComp';
FpComp := PCBServer.CreatePCBLibComp;
FpComp.Name := FpName;
PcbLib.RegisterComponent(FpComp);
GStage := 'PreProcess';
PCBServer.PreProcess;
GStage := 'pads';
MakePad('1', -30); { centers ~1.5 mm apart }
MakePad('2', 30);
GStage := 'PostProcess';
PCBServer.PostProcess;
PcbLib.Board.ViewManager_FullUpdate;
GStage := 'save';
SavedOK := False;
if (PcbPath <> '') and (LastServerDoc <> nil) then
begin
LastServerDoc.SetFileName(PcbPath);
SavedOK := LastServerDoc.DoFileSave('');
end;
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
GStage := 'done';
RespOk(', "footprint": ' + JStr(FpName) + ', "pads": 2, "saved": ' + SavedStr +
', "saved_to": ' + JStr(PcbPath));
end;
{ ---- command: build/append a symbol in a SchLib with N pins + a footprint link.
pins as pin0..padN (CommaText): designator,name,x,y,orient(0/90/180/270) mils. ---- }
procedure CmdBuildSymbol;
var
SD : IServerDocument;
Lib : ISch_Lib;
Cmp : ISch_Component;
R : ISch_Rectangle;
El : ISch_Ellipse;
Pin : ISch_Pin;
Impl : ISch_Implementation;
flds : TStringList;
SchPath, CompName, FpName, DesigPfx, v, SavedStr : String;
npins, i, ori : Integer;
SavedOK : Boolean;
begin
SchPath := Req.Values['schlib'];
CompName := Req.Values['component']; if CompName = '' then CompName := 'COMP';
FpName := Req.Values['footprint'];
DesigPfx := Req.Values['designator']; if DesigPfx = '' then DesigPfx := 'U';
npins := StrToIntDef(Req.Values['pincount'], 1);
GStage := 'open-or-create SchLib';
if (SchPath <> '') and FileExists(SchPath) then
SD := Client.OpenDocument('SCHLIB', SchPath)
else
SD := CreateNewDocumentFromDocumentKind('SchLib');
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
Lib := SchServer.GetCurrentSchDocument;
if Lib = nil then begin RespErr('no SchLib'); Exit; end;
GStage := 'create component';
Cmp := SchServer.SchObjectFactory(eSchComponent, eCreate_Default);
Cmp.CurrentPartID := 1;
Cmp.DisplayMode := 0;
Cmp.LibReference := CompName;
GStage := 'body';
if Req.Values['body'] = 'circle' then
begin
El := SchServer.SchObjectFactory(eEllipse, eCreate_Default);
El.Location := Point(MilsToCoord(50), MilsToCoord(0));
El.Radius := MilsToCoord(50);
El.SecondaryRadius := MilsToCoord(50);
El.LineWidth := eSmall;
El.OwnerPartId := Cmp.CurrentPartID;
El.OwnerPartDisplayMode := Cmp.DisplayMode;
Cmp.AddSchObject(El);
end
else
begin
R := SchServer.SchObjectFactory(eRectangle, eCreate_Default);
R.LineWidth := eSmall;
R.Location := Point(MilsToCoord(0), MilsToCoord(-50));
R.Corner := Point(MilsToCoord(200), MilsToCoord(50));
R.OwnerPartId := Cmp.CurrentPartID;
R.OwnerPartDisplayMode := Cmp.DisplayMode;
Cmp.AddSchObject(R);
end;
GStage := 'pins';
flds := TStringList.Create;
try
for i := 0 to npins - 1 do
begin
v := Req.Values['pin' + IntToStr(i)];
if v = '' then Continue;
flds.CommaText := v; { designator,name,x,y,orient }
Pin := SchServer.SchObjectFactory(ePin, eCreate_Default);
Pin.Location := Point(MilsToCoord(StrToIntDef(flds[2], -100)),
MilsToCoord(StrToIntDef(flds[3], 0)));
ori := StrToIntDef(flds[4], 0);
if ori = 90 then Pin.Orientation := eRotate90
else if ori = 180 then Pin.Orientation := eRotate180
else if ori = 270 then Pin.Orientation := eRotate270
else Pin.Orientation := eRotate0;
Pin.Designator := flds[0];
Pin.Name := flds[1];
Pin.OwnerPartId := Cmp.CurrentPartID;
Pin.OwnerPartDisplayMode := Cmp.DisplayMode;
Cmp.AddSchObject(Pin);
end;
finally
flds.Free;
end;
GStage := 'designator + params';
Cmp.Designator.Text := DesigPfx + '?';
if Req.Values['value'] <> '' then Cmp.Comment.Text := Req.Values['value'];
MaybeSet(Cmp, 'LCSC Part #', 'lcsc');
MaybeSet(Cmp, 'Manufacturer Part Number', 'mpn');
MaybeSet(Cmp, 'Manufacturer', 'manufacturer');
MaybeSet(Cmp, 'Value', 'value');
MaybeSet(Cmp, 'Description', 'description');
GStage := 'footprint link';
if FpName <> '' then
begin
Impl := Cmp.AddSchImplementation;
Impl.ClearAllDatafileLinks;
Impl.ModelName := FpName;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(FpName, '', 'PCBLIB');
Impl.IsCurrent := True;
end;
GStage := 'AddSchComponent';
Lib.AddSchComponent(Cmp);
SchServer.RobotManager.SendMessage(Lib.I_ObjectAddress, c_BroadCast,
SCHM_PrimitiveRegistration, Cmp.I_ObjectAddress);
GStage := 'save';
SavedOK := False;
if (SchPath <> '') and (LastServerDoc <> nil) then
begin
LastServerDoc.SetFileName(SchPath);
SavedOK := LastServerDoc.DoFileSave('');
end;
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "component": ' + JStr(CompName) + ', "footprint": ' + JStr(FpName) +
', "pins": ' + IntToStr(npins) + ', "saved": ' + SavedStr);
end;
{ ---- command: build an Altium footprint from a pad spec + optional STEP 3D body.
pads passed as pad0..padN (CommaText): smd,x,y,w,h,drill,name (all mils). ---- }
procedure CmdBuildFootprint;
var
SD : IServerDocument;
PcbLib : IPCB_Library;
Fp : IPCB_LibComponent;
Pad : IPCB_Pad;
Body : IPCB_ComponentBody;
Mdl : IPCB_Model;
PcbPath, FpName, StepPath, v, SavedStr, BodyNote : String;
n, i : Integer;
flds : TStringList;
SavedOK : Boolean;
begin
PcbPath := Req.Values['pcblib'];
FpName := Req.Values['footprint']; if FpName = '' then FpName := 'FP';
StepPath := Req.Values['step'];
n := StrToIntDef(Req.Values['padcount'], 0);
GStage := 'open-or-create PcbLib doc';
if (PcbPath <> '') and FileExists(PcbPath) then
SD := Client.OpenDocument('PCBLIB', PcbPath) { append to existing lib }
else
SD := CreateNewDocumentFromDocumentKind('PcbLib');
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
GStage := 'GetCurrentPCBLibrary';
if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;
PcbLib := PCBServer.GetCurrentPCBLibrary;
if PcbLib = nil then begin RespErr('no current PcbLib'); Exit; end;
GStage := 'CreatePCBLibComp';
Fp := PCBServer.CreatePCBLibComp;
Fp.Name := FpName;
PcbLib.RegisterComponent(Fp);
PCBServer.PreProcess;
GStage := 'pads';
flds := TStringList.Create;
try
for i := 0 to n - 1 do
begin
v := Req.Values['pad' + IntToStr(i)];
if v = '' then Continue;
flds.CommaText := v; { smd,x,y,w,h,drill,name,shape (mils) }
Pad := PCBServer.PCBObjectFactory(ePadObject, eNoDimension, eCreate_Default);
Pad.X := MilsToCoord(StrToIntDef(flds[1], 0));
Pad.Y := MilsToCoord(StrToIntDef(flds[2], 0));
Pad.TopXSize := MilsToCoord(StrToIntDef(flds[3], 50));
Pad.TopYSize := MilsToCoord(StrToIntDef(flds[4], 50));
if (flds.Count >= 8) and ((flds[7] = 'circle') or (flds[7] = 'oval')) then
Pad.TopShape := eRounded
else
Pad.TopShape := eRectangular;
if flds[0] = '1' then
begin
Pad.HoleSize := 0;
Pad.Layer := eTopLayer; { SMD }
end
else
begin
Pad.HoleSize := MilsToCoord(StrToIntDef(flds[5], 0));
Pad.Layer := eMultiLayer; { through-hole }
end;
Pad.Name := flds[6];
Fp.AddPCBObject(Pad);
PCBServer.SendMessageToRobots(Fp.I_ObjectAddress, c_Broadcast,
PCBM_BoardRegisteration, Pad.I_ObjectAddress);
end;
finally
flds.Free;
end;
GStage := 'step body';
BodyNote := 'none';
if StepPath <> '' then
begin
Body := PCBServer.PCBObjectFactory(eComponentBodyObject, eNoDimension, eCreate_Default);
Mdl := Body.ModelFactory_FromFilename(StepPath, False);
if Mdl <> nil then
begin
Body.SetState_FromModel;
Body.Model := Mdl;
Fp.AddPCBObject(Body);
BodyNote := 'added';
end
else BodyNote := 'model load failed';
end;
GStage := 'PostProcess';
PCBServer.PostProcess;
GStage := 'save';
SavedOK := False;
if (PcbPath <> '') and (LastServerDoc <> nil) then
begin
LastServerDoc.SetFileName(PcbPath);
SavedOK := LastServerDoc.DoFileSave('');
end;
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "footprint": ' + JStr(FpName) + ', "pads": ' + IntToStr(n) +
', "step_body": ' + JStr(BodyNote) + ', "saved": ' + SavedStr +
', "saved_to": ' + JStr(PcbPath));
end;
{ ---- command: compile a Library Package (.LibPkg) into a .IntLib ---- }
procedure CmdCompileIntLib;
var
LibPkgPath, OutDir : String;
Proj : IProject;
begin
LibPkgPath := Req.Values['libpkg'];
OutDir := Req.Values['output'];
if LibPkgPath = '' then begin RespErr('missing libpkg'); Exit; end;
GStage := 'DM_OpenProject';
Proj := GetWorkspace.DM_OpenProject(LibPkgPath, True);
if Proj = nil then begin RespErr('could not open LibPkg: ' + LibPkgPath); Exit; end;
GStage := 'DM_Compile';
Proj.DM_Compile;
GStage := 'CreateIntegratedLibrary';
IntegratedLibraryManager.CreateIntegratedLibrary(Proj, OutDir, True);
GStage := 'done';
RespOk(', "libpkg": ' + JStr(LibPkgPath) + ', "output_dir": ' + JStr(OutDir));
end;
{ ---- command: extract the editable sources (SchLib + PcbLib) from a compiled
.IntLib, then report the extracted document paths so the caller can edit
them (e.g. add an LCSC param) before re-installing/compiling. ---- }
procedure CmdExtractIntLib;
var
IntLibPath, P, SchAcc, PcbAcc : String;
WS : IWorkspace;
Proj : IProject;
Doc : IDocument;
i, j : Integer;
FirstS, FirstP : Boolean;
begin
IntLibPath := Req.Values['intlib'];
if IntLibPath = '' then begin RespErr('missing intlib'); Exit; end;
GStage := 'ExtractSources';
IntegratedLibraryManager.ExtractSources(IntLibPath);
GStage := 'scan projects';
WS := GetWorkspace;
SchAcc := ''; PcbAcc := '';
FirstS := True; FirstP := True;
if WS <> nil then
for i := 0 to WS.DM_ProjectCount - 1 do
begin
Proj := WS.DM_Projects(i);
if Proj = nil then Continue;
for j := 0 to Proj.DM_LogicalDocumentCount - 1 do
begin
Doc := Proj.DM_LogicalDocuments(j);
if Doc = nil then Continue;
P := Doc.DM_FullPath;
if Pos('.SchLib', P) > 0 then
begin
if not FirstS then SchAcc := SchAcc + ', ';
SchAcc := SchAcc + JStr(P); FirstS := False;
end
else if Pos('.PcbLib', P) > 0 then
begin
if not FirstP then PcbAcc := PcbAcc + ', ';
PcbAcc := PcbAcc + JStr(P); FirstP := False;
end;
end;
end;
GStage := 'done';
RespOk(', "intlib": ' + JStr(IntLibPath) +
', "schlibs": [ ' + SchAcc + ' ]' +
', "pcblibs": [ ' + PcbAcc + ' ]');
end;
{ ---- helper: does S contain any of the comma-separated lowercase needles? ---- }
function ContainsAny(const S, CsvLower : String) : Boolean;
var parts : TStringList; i : Integer;
begin
Result := False;
parts := TStringList.Create;
try
parts.CommaText := CsvLower;
for i := 0 to parts.Count - 1 do
if (parts[i] <> '') and (Pos(parts[i], S) > 0) then
begin Result := True; Break; end;
finally
parts.Free;
end;
end;
{ ---- command: list open documents — disabled (Client.DocumentCount is not a
valid DelphiScript identifier on AD26; calling it threw a script-error
dialog). Returns an empty list instead of raising. ---- }
procedure CmdListDocs;
begin
RespOk(', "docs": [], "note": "doc enumeration unsupported on this Altium build"');
end;
{ ---- command: rename a component in a SchLib (in place), optionally updating its
Description, footprint link, and designator. Preserves the exact symbol
graphics/pins/params — only identity/metadata changes. ---- }
procedure CmdRenameComponent;
var
SchPath, OldName, NewName, FpName, Desig, SavedStr : String;
Lib : ISch_Lib;
Cmp : ISch_Component;
It : ISch_Iterator;
Impl : ISch_Implementation;
Found : Boolean;
SavedOK : Boolean;
begin
SchPath := Req.Values['schlib'];
OldName := Req.Values['component'];
NewName := Req.Values['newname'];
FpName := Req.Values['footprint'];
Desig := Req.Values['designator'];
if (SchPath = '') or (OldName = '') or (NewName = '') then
begin RespErr('need schlib + component + newname'); Exit; end;
GStage := 'open schlib';
Lib := OpenSchLibDoc(SchPath);
if Lib = nil then begin RespErr('could not open: ' + SchPath); Exit; end;
GStage := 'find component';
Cmp := FindComponent(Lib, OldName);
if Cmp = nil then begin RespErr('component not found: ' + OldName); Exit; end;
GStage := 'set identity';
Cmp.LibReference := NewName;
Cmp.DesignItemID := NewName;
if Desig <> '' then Cmp.Designator.Text := Desig;
GStage := 'description';
MaybeSet(Cmp, 'Description', 'description');
if FpName <> '' then
begin
GStage := 'update footprint link';
Found := False;
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eImplementation));
try
Impl := It.FirstSchObject;
while Impl <> nil do
begin
Impl.ClearAllDatafileLinks;
Impl.ModelName := FpName;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(FpName, '', 'PCBLIB');
Impl.IsCurrent := True;
Found := True;
Impl := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
if not Found then
begin
Impl := Cmp.AddSchImplementation;
Impl.ClearAllDatafileLinks;
Impl.ModelName := FpName;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(FpName, '', 'PCBLIB');
Impl.IsCurrent := True;
end;
end;
GStage := 'save';
Lib.GraphicallyInvalidate;
SavedOK := False;
if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "renamed": ' + JStr(OldName) + ' -> ' + JStr(NewName) +
', "footprint": ' + JStr(FpName) + ', "saved": ' + SavedStr);
end;
{ ---- command: rename a footprint (IPCB_LibComponent) inside a PcbLib, in place ---- }
procedure CmdRenameFootprint;
var
PcbPath, OldName, NewName, SavedStr : String;
SD : IServerDocument;
PcbLib : IPCB_Library;
Fp : IPCB_LibComponent;
i : Integer;
Found : Boolean;
SavedOK : Boolean;
begin
PcbPath := Req.Values['pcblib'];
OldName := Req.Values['footprint'];
NewName := Req.Values['newname'];
if (PcbPath = '') or (NewName = '') then
begin RespErr('need pcblib + newname'); Exit; end;
GStage := 'open pcblib';
SD := Client.OpenDocument('PCBLIB', PcbPath);
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;
PcbLib := PCBServer.GetCurrentPCBLibrary;
if PcbLib = nil then begin RespErr('no current PcbLib'); Exit; end;
GStage := 'rename component';
Found := False;
for i := 0 to PcbLib.ComponentCount - 1 do
begin
Fp := PcbLib.GetComponent(i);
if Fp = nil then Continue;
if (OldName = '') or (Fp.Name = OldName) then
begin
Fp.Name := NewName;
Found := True;
if OldName <> '' then Break; { rename only the first if OldName given }
end;
end;
if not Found then begin RespErr('footprint not found: ' + OldName); Exit; end;
GStage := 'save';
PcbLib.Board.ViewManager_FullUpdate;
SavedOK := False;
if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "renamed_footprint": ' + JStr(OldName) + ' -> ' + JStr(NewName) +
', "saved": ' + SavedStr);
end;
{ ---- command: import a part in ONE round-trip. Given the already-extracted source
SchLib+PcbLib, it: renames the footprint -> friendly name + saves-as into the
target PcbLib; attaches LCSC/identity params + renames the symbol + links it
to the footprint + saves-as into the target SchLib; installs both; and reads
the params back. Collapses ~8 separate X2 launches into one. Every primitive
used here is already proven by the per-step verbs. ---- }
procedure CmdImportPart;
var
SrcSch, SrcPcb, TgtSch, TgtPcb, NewName, NewFp, Desig, OldComp : String;
PcbSD, SchSD : IServerDocument;
PcbLib : IPCB_Library;
Fp : IPCB_LibComponent;
SchLib : ISch_Lib;
Cmp : ISch_Component;
It : ISch_Iterator;
Impl : ISch_Implementation;
i : Integer;
FoundFp, FoundImpl, Saved1, Saved2 : Boolean;
begin
SrcSch := Req.Values['src_sch'];
SrcPcb := Req.Values['src_pcb'];
TgtSch := Req.Values['tgt_sch'];
TgtPcb := Req.Values['tgt_pcb'];
NewName := Req.Values['name'];
NewFp := Req.Values['footprint'];
Desig := Req.Values['designator']; if Desig = '' then Desig := 'U?';
if (SrcSch = '') or (SrcPcb = '') or (TgtSch = '') or (TgtPcb = '')
or (NewName = '') or (NewFp = '') then
begin RespErr('need src_sch, src_pcb, tgt_sch, tgt_pcb, name, footprint'); Exit; end;
{ 1) footprint: rename first footprint -> NewFp, save-as into target PcbLib }
GStage := 'open src pcb';
PcbSD := Client.OpenDocument('PCBLIB', SrcPcb);
if PcbSD <> nil then Client.ShowDocument(PcbSD);
if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;
PcbLib := PCBServer.GetCurrentPCBLibrary;
if PcbLib = nil then begin RespErr('no current PcbLib'); Exit; end;
GStage := 'rename footprint';
FoundFp := False;
for i := 0 to PcbLib.ComponentCount - 1 do
begin
Fp := PcbLib.GetComponent(i);
if Fp <> nil then begin Fp.Name := NewFp; FoundFp := True; Break; end;
end;
if not FoundFp then begin RespErr('no footprint in ' + SrcPcb); Exit; end;
PcbLib.Board.ViewManager_FullUpdate;
GStage := 'save pcb as target';
PcbSD.SetFileName(TgtPcb);
Saved1 := PcbSD.DoFileSave('');
{ 2) symbol: params + rename + footprint link, save-as into target SchLib }
GStage := 'open src sch';
SchSD := Client.OpenDocument('SCHLIB', SrcSch);
if SchSD <> nil then Client.ShowDocument(SchSD);
SchLib := SchServer.GetSchDocumentByPath(SrcSch);
if SchLib = nil then begin RespErr('open src sch failed'); Exit; end;
LastServerDoc := SchSD;
GStage := 'find component';
Cmp := FindComponent(SchLib, ''); { first component }
if Cmp = nil then begin RespErr('no component in ' + SrcSch); Exit; end;
OldComp := Cmp.LibReference;
GStage := 'params';
MaybeSet(Cmp, 'LCSC Part #', 'lcsc');
MaybeSet(Cmp, 'Manufacturer Part Number', 'mpn');
MaybeSet(Cmp, 'Manufacturer', 'manufacturer');
MaybeSet(Cmp, 'Value', 'value');
MaybeSet(Cmp, 'Description', 'description');
MaybeSet(Cmp, 'Package', 'package');
GStage := 'rename + link';
Cmp.LibReference := NewName;
Cmp.DesignItemID := NewName;
Cmp.Designator.Text := Desig;
FoundImpl := False;
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eImplementation));
try
Impl := It.FirstSchObject;
while Impl <> nil do
begin
Impl.ClearAllDatafileLinks;
Impl.ModelName := NewFp;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(NewFp, '', 'PCBLIB');
Impl.IsCurrent := True;
FoundImpl := True;
Impl := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
if not FoundImpl then
begin
Impl := Cmp.AddSchImplementation;
Impl.ClearAllDatafileLinks;
Impl.ModelName := NewFp;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(NewFp, '', 'PCBLIB');
Impl.IsCurrent := True;
end;
SchLib.GraphicallyInvalidate;
GStage := 'save sch as target';
SchSD.SetFileName(TgtSch);
Saved2 := SchSD.DoFileSave('');
GStage := 'install';
IntegratedLibraryManager.InstallLibrary(TgtPcb);
IntegratedLibraryManager.InstallLibrary(TgtSch);
GStage := 'done';
RespOk(', "component": ' + JStr(NewName) + ', "footprint": ' + JStr(NewFp) +
', "from": ' + JStr(OldComp) +
', "saved_pcb": ' + BoolStr(Saved1) + ', "saved_sch": ' + BoolStr(Saved2) +
', "params": ' + ParamsJSON(Cmp));
end;
{ ---- command: remove a component (by LibReference) from a SchLib, then save ---- }
procedure CmdRemoveComponent;
var SchLibPath, CompName : String; Lib : ISch_Lib; Cmp, Target : ISch_Component;
It : ISch_Iterator; SavedOK : Boolean;
begin
SchLibPath := Req.Values['schlib'];
CompName := Req.Values['component'];
if (SchLibPath = '') or (CompName = '') then begin RespErr('need schlib + component'); Exit; end;
GStage := 'open schlib';
Lib := OpenSchLibDoc(SchLibPath);
if Lib = nil then begin RespErr('could not open: ' + SchLibPath); Exit; end;
GStage := 'find component';
Target := nil;
It := Lib.SchLibIterator_Create;
It.AddFilter_ObjectSet(MkSet(eSchComponent));
try
Cmp := It.FirstSchObject;
while Cmp <> nil do
begin
if Cmp.LibReference = CompName then begin Target := Cmp; Break; end;
Cmp := It.NextSchObject;
end;
finally
Lib.SchIterator_Destroy(It);
end;
if Target = nil then begin RespErr('component not found: ' + CompName); Exit; end;
GStage := 'remove component';
Lib.RemoveSchComponent(Target);
GStage := 'save';
SavedOK := False;
if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then RespOk(', "removed_component": ' + JStr(CompName) + ', "saved": true')
else RespOk(', "removed_component": ' + JStr(CompName) + ', "saved": false');
end;
{ ---- command: close an open document by its file path (no save prompt if unmodified) ---- }
procedure CmdCloseDoc;
var Path, Kind : String; SD : IServerDocument;
begin
Path := Req.Values['path'];
if Path = '' then begin RespErr('missing path'); Exit; end;
GStage := 'get handle';
Kind := 'SCHLIB';
if Pos('.pcblib', LowerCase(Path)) > 0 then Kind := 'PCBLIB';
SD := Client.OpenDocument(Kind, Path);
if SD = nil then begin RespOk(', "closed": false, "note": "no handle for path"'); Exit; end;
GStage := 'CloseDocument';
Client.CloseDocument(SD);
RespOk(', "closed": true, "kind": ' + JStr(Kind) + ', "path": ' + JStr(Path));
end;
{ ---- command: list all components (LibReference) in a SchLib ---- }
procedure CmdListSymbols;
var SchLibPath, Acc : String; Lib : ISch_Lib; Cmp : ISch_Component; It : ISch_Iterator; First : Boolean;
begin
SchLibPath := Req.Values['schlib'];
Lib := OpenSchLibDoc(SchLibPath);
if Lib = nil then begin RespErr('could not open: ' + SchLibPath); Exit; end;
Acc := ''; First := True;
It := Lib.SchLibIterator_Create;
It.AddFilter_ObjectSet(MkSet(eSchComponent));
try
Cmp := It.FirstSchObject;
while Cmp <> nil do
begin
if not First then Acc := Acc + ', ';
Acc := Acc + JStr(Cmp.LibReference);
First := False;
Cmp := It.NextSchObject;
end;
finally
Lib.SchIterator_Destroy(It);
end;
RespOk(', "components": [ ' + Acc + ' ]');
end;
{ ---- command: clone a template component's graphics exactly into a new component,
set a fresh footprint link + designator. ---- }
procedure CmdCloneSymbol;
var
SchPath, TemplateName, NewName, FpName, DesigPfx, SavedStr : String;
Lib : ISch_Lib;
Tmpl, NewCmp : ISch_Component;
It : ISch_Iterator;
Prim, NewPrim : ISch_GraphicalObject;
Impl : ISch_Implementation;
SavedOK : Boolean;
o : Integer;
begin
SchPath := Req.Values['schlib'];
TemplateName := Req.Values['template'];
NewName := Req.Values['component'];
FpName := Req.Values['footprint'];
DesigPfx := Req.Values['designator']; if DesigPfx = '' then DesigPfx := 'MC';
GStage := 'open schlib';
Lib := OpenSchLibDoc(SchPath);
if Lib = nil then begin RespErr('open failed'); Exit; end;
GStage := 'find template';
Tmpl := FindComponent(Lib, TemplateName);
if Tmpl = nil then begin RespErr('template not found: ' + TemplateName); Exit; end;
GStage := 'create new component';
NewCmp := SchServer.SchObjectFactory(eSchComponent, eCreate_Default);
NewCmp.CurrentPartID := 1;
NewCmp.DisplayMode := 0;
NewCmp.LibReference := NewName;
GStage := 'copy graphics by ObjectId';
It := Tmpl.SchIterator_Create; { no type filter — pick graphics by ObjectId }
try
Prim := It.FirstSchObject;
while Prim <> nil do
begin
o := Prim.ObjectId;
{ copy pins + ALL graphics; skip only metadata that's set explicitly:
25=designator, 27=parameter, 45=comment, 46=implementation }
if (o <> 25) and (o <> 27) and (o <> 45) and (o <> 46) then
begin
NewPrim := Prim.Replicate;
NewPrim.OwnerPartId := NewCmp.CurrentPartID;
NewPrim.OwnerPartDisplayMode := NewCmp.DisplayMode;
NewCmp.AddSchObject(NewPrim);
end;
Prim := It.NextSchObject;
end;
finally
Tmpl.SchIterator_Destroy(It);
end;
GStage := 'designator';
NewCmp.Designator.Text := DesigPfx + '?';
GStage := 'footprint link';
if FpName <> '' then
begin
Impl := NewCmp.AddSchImplementation;
Impl.ClearAllDatafileLinks;
Impl.ModelName := FpName;
Impl.ModelType := 'PCBLIB';
Impl.AddDataFileLink(FpName, '', 'PCBLIB');
Impl.IsCurrent := True;
end;
GStage := 'AddSchComponent';
Lib.AddSchComponent(NewCmp);
SchServer.RobotManager.SendMessage(Lib.I_ObjectAddress, c_BroadCast,
SCHM_PrimitiveRegistration, NewCmp.I_ObjectAddress);
GStage := 'save';
SavedOK := False;
if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "cloned": ' + JStr(TemplateName) + ' -> ' + JStr(NewName) +
', "footprint": ' + JStr(FpName) + ', "saved": ' + SavedStr);
end;
{ ---- command: dump a component's primitives (ObjectId + location) ---- }
procedure CmdReadSymbol;
var SchLibPath, CompName, Acc : String; Lib : ISch_Lib; Cmp : ISch_Component;
It : ISch_Iterator; Prim : ISch_GraphicalObject; First : Boolean;
begin
SchLibPath := Req.Values['schlib']; CompName := Req.Values['component'];
Lib := OpenSchLibDoc(SchLibPath);
if Lib = nil then begin RespErr('open failed'); Exit; end;
Cmp := FindComponent(Lib, CompName);
if Cmp = nil then begin RespErr('component not found: ' + CompName); Exit; end;
Acc := ''; First := True;
It := Cmp.SchIterator_Create;
try
Prim := It.FirstSchObject;
while Prim <> nil do
begin
if not First then Acc := Acc + ', ';
Acc := Acc + IntToStr(Prim.ObjectId);
First := False;
Prim := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
RespOk(', "component": ' + JStr(CompName) + ', "primitives": [ ' + Acc + ' ]');
end;
{ ---- command: install a file-based library (.SchLib/.PcbLib/.IntLib) into Available Libraries ---- }
procedure CmdInstallLibrary;
var Path : String;
begin
Path := Req.Values['path'];
if Path = '' then begin RespErr('missing path'); Exit; end;
GStage := 'InstallLibrary';
IntegratedLibraryManager.InstallLibrary(Path);
RespOk(', "installed": ' + JStr(Path));
end;
{ ---- command: uninstall available libraries whose path matches any needle ---- }
procedure CmdCleanupLibs;
var Match, Path, Removed : String; i, n : Integer;
begin
Match := LowerCase(Req.Values['match']);
if Match = '' then begin RespErr('missing match (comma-separated path substrings)'); Exit; end;
Removed := '';
GStage := 'InstalledLibraryCount';
n := IntegratedLibraryManager.InstalledLibraryCount;
GStage := 'iterate+uninstall';
for i := n - 1 downto 0 do
begin
Path := IntegratedLibraryManager.InstalledLibraryPath(i);
if ContainsAny(LowerCase(Path), Match) then
begin
IntegratedLibraryManager.UninstallLibrary(Path);
if Removed <> '' then Removed := Removed + ', ';
Removed := Removed + JStr(Path);
end;
end;
RespOk(', "removed": [ ' + Removed + ' ]');
end;
procedure CmdPing;
var WS : IWorkspace; n : Integer;
begin
n := -1;
try
WS := GetWorkspace;
if WS <> nil then n := WS.DM_ProjectCount;
except
end;
RespOk(', "addin": "AdomAltium", "open_project_count": ' + IntToStr(n));
end;
{ ---- command: attach a simple extruded 3D body (a box) to a footprint, sized in
mils. Gives passives a 3D model without needing a STEP file. ---- }
procedure CmdAddBody;
var
PcbPath, FpName, SavedStr : String;
SD : IServerDocument;
PcbLib : IPCB_Library;
Fp : IPCB_LibComponent;
Body : IPCB_ComponentBody;
Contour : IPCB_Contour;
bw, bh, hgt, soff, i : Integer;
Found, SavedOK : Boolean;
begin
PcbPath := Req.Values['pcblib'];
FpName := Req.Values['footprint'];
bw := StrToIntDef(Req.Values['bw_mil'], 39); { body length, mils }
bh := StrToIntDef(Req.Values['bh_mil'], 20); { body width, mils }
hgt := StrToIntDef(Req.Values['h_mil'], 16); { body height, mils }
soff := StrToIntDef(Req.Values['so_mil'], 1); { standoff, mils }
if (PcbPath = '') or (FpName = '') then begin RespErr('need pcblib + footprint'); Exit; end;
GStage := 'open pcblib';
SD := Client.OpenDocument('PCBLIB', PcbPath);
if SD <> nil then Client.ShowDocument(SD);
LastServerDoc := SD;
if PCBServer = nil then begin RespErr('PCBServer nil'); Exit; end;
PcbLib := PCBServer.GetCurrentPCBLibrary;
if PcbLib = nil then begin RespErr('no current PcbLib'); Exit; end;
GStage := 'find footprint';
Found := False;
for i := 0 to PcbLib.ComponentCount - 1 do
begin
Fp := PcbLib.GetComponent(i);
if (Fp <> nil) and (Fp.Name = FpName) then begin Found := True; Break; end;
end;
if not Found then begin RespErr('footprint not found: ' + FpName); Exit; end;
GStage := 'create body';
PCBServer.PreProcess;
Body := PCBServer.PCBObjectFactory(eComponentBodyObject, eNoDimension, eCreate_Default);
Body.BodyProjection := eBoardSide_Top;
Body.StandoffHeight := MilsToCoord(soff);
Body.OverallHeight := MilsToCoord(soff + hgt);
Body.BodyColor3D := $00202020;
GStage := 'outline';
Contour := PCBServer.PCBContourFactory;
Contour.AddPoint(MilsToCoord(-(bw div 2)), MilsToCoord(-(bh div 2)));
Contour.AddPoint(MilsToCoord( (bw div 2)), MilsToCoord(-(bh div 2)));
Contour.AddPoint(MilsToCoord( (bw div 2)), MilsToCoord( (bh div 2)));
Contour.AddPoint(MilsToCoord(-(bw div 2)), MilsToCoord( (bh div 2)));
Body.SetOutlineContour(Contour);
GStage := 'add to footprint';
Fp.AddPCBObject(Body);
PCBServer.SendMessageToRobots(Fp.I_ObjectAddress, c_Broadcast,
PCBM_BoardRegisteration, Body.I_ObjectAddress);
PCBServer.PostProcess;
GStage := 'save';
LastServerDoc.SetFileName(PcbPath);
SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "footprint": ' + JStr(FpName) + ', "body": "extruded", "saved": ' + SavedStr);
end;
{ ---- command: set which parameters are VISIBLE on a component. Names in 'visible'
(comma list) stay shown; every other parameter is hidden, and the Comment is
hidden too unless 'Comment' is listed. Controls what appears when the symbol
is placed on a schematic. ---- }
procedure CmdSetVisibility;
var
SchPath, CompRef, Vis, SavedStr : String;
Lib : ISch_Lib;
Cmp : ISch_Component;
It : ISch_Iterator;
P : ISch_Parameter;
visList : TStringList;
SavedOK : Boolean;
shown, hidden : Integer;
begin
SchPath := Req.Values['schlib'];
CompRef := Req.Values['component'];
Vis := Req.Values['visible']; if Vis = '' then Vis := 'Value';
if SchPath = '' then begin RespErr('missing schlib'); Exit; end;
GStage := 'open schlib';
Lib := OpenSchLibDoc(SchPath);
if Lib = nil then begin RespErr('could not open: ' + SchPath); Exit; end;
Cmp := FindComponent(Lib, CompRef);
if Cmp = nil then begin RespErr('component not found: ' + CompRef); Exit; end;
visList := TStringList.Create;
visList.CommaText := Vis; { case-insensitive IndexOf by default }
shown := 0; hidden := 0;
try
GStage := 'set parameter visibility';
It := Cmp.SchIterator_Create;
It.AddFilter_ObjectSet(MkSet(eParameter));
try
P := It.FirstSchObject;
while P <> nil do
begin
if visList.IndexOf(P.Name) >= 0 then
begin P.IsHidden := False; Inc(shown); end
else
begin P.IsHidden := True; Inc(hidden); end;
P := It.NextSchObject;
end;
finally
Cmp.SchIterator_Destroy(It);
end;
GStage := 'comment visibility';
if visList.IndexOf('Comment') < 0 then Cmp.Comment.IsHidden := True
else Cmp.Comment.IsHidden := False;
finally
visList.Free;
end;
GStage := 'save';
Lib.GraphicallyInvalidate;
SavedOK := False;
if LastServerDoc <> nil then SavedOK := LastServerDoc.DoFileSave('');
if SavedOK then SavedStr := 'true' else SavedStr := 'false';
RespOk(', "component": ' + JStr(Cmp.LibReference) + ', "visible": ' + JStr(Vis) +
', "shown": ' + IntToStr(shown) + ', "hidden": ' + IntToStr(hidden) +
', "saved": ' + SavedStr);
end;
{ ---- shared command dispatch. Assumes the global Req is loaded and GReqId set. ---- }
procedure Dispatch;
var Cmd : String;
begin
try
Cmd := Req.Values['command'];
GStage := 'dispatch ' + Cmd;
if Cmd = 'ping' then CmdPing
else if Cmd = 'add_lcsc_param' then CmdAddLcscParam
else if Cmd = 'read_params' then CmdReadParams
else if Cmd = 'create_and_attach' then CmdCreateAndAttach
else if Cmd = 'compile_intlib' then CmdCompileIntLib
else if Cmd = 'extract_intlib' then CmdExtractIntLib
else if Cmd = 'cleanup_libs' then CmdCleanupLibs
else if Cmd = 'create_resistor' then CmdCreateResistor
else if Cmd = 'list_docs' then CmdListDocs
else if Cmd = 'remove_component' then CmdRemoveComponent
else if Cmd = 'close_doc' then CmdCloseDoc
else if Cmd = 'build_footprint' then CmdBuildFootprint
else if Cmd = 'build_symbol' then CmdBuildSymbol
else if Cmd = 'install_library' then CmdInstallLibrary
else if Cmd = 'list_symbols' then CmdListSymbols
else if Cmd = 'read_symbol' then CmdReadSymbol
else if Cmd = 'clone_symbol' then CmdCloneSymbol
else if Cmd = 'rename_component' then CmdRenameComponent
else if Cmd = 'rename_footprint' then CmdRenameFootprint
else if Cmd = 'import_part' then CmdImportPart
else if Cmd = 'add_body' then CmdAddBody
else if Cmd = 'set_visibility' then CmdSetVisibility
else RespErr('unknown command: ' + Cmd);
except
RespErr('exception at stage: ' + GStage);
end;
end;
{ ---- one-shot entry (X2 ...>Run): read request.txt, dispatch, write response. ---- }
procedure Run;
begin
Req := TStringList.Create;
try
try
if FileExists(EX_DIR + 'request.txt') then
Req.LoadFromFile(EX_DIR + 'request.txt');
GReqId := Req.Values['id'];
Dispatch;
except
RespErr('exception at stage: ' + GStage);
end;
finally
Req.Free;
end;
end;