Перепиновка под будущий аналог
+скрипт для конвертации падов под CNC
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
/tests/
|
||||
/PCB/Project Logs*/
|
||||
/*/__Previews
|
||||
/*/History
|
||||
/*/Project Logs*/
|
||||
__Previews/
|
||||
History
|
||||
Project Logs*/
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
62
PCB/Scripts/RivetViaConverter/README.md
Normal file
62
PCB/Scripts/RivetViaConverter/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Rivet Via Converter для Altium Designer
|
||||
|
||||
Скрипт анализирует все отверстия активного `PcbDoc` и преобразует в размеры
|
||||
имеющихся заклёпок как сквозные Via, так и все сверлёные Pad. Обрабатываются
|
||||
свободные объекты на плате и примитивы внутри футпринтов:
|
||||
|
||||
| Исходный Hole Size | Новый Hole Size | Диаметр меди | Диаметр зоны изоляции |
|
||||
|---:|---:|---:|---:|
|
||||
| `<= 0,3 мм` | `0,9 мм` | `1,7 мм` | `2,2 мм` |
|
||||
| `<= 1,1 мм` | `1,3 мм` | `2,1 мм` | `2,7 мм` |
|
||||
| `> 1,1 мм` | `2,0 мм` | `2,8 мм` | `3,7 мм` |
|
||||
|
||||
Дополнительный запас к внешнему диаметру не добавляется. Глухие и скрытые Via
|
||||
не изменяются. Также игнорируются неплакированные Pad и отверстия, у которых
|
||||
внешний размер площадки равен диаметру отверстия, то есть отсутствует медное
|
||||
кольцо. SMD Pad с нулевым размером отверстия не изменяются. Диаметр меди всегда
|
||||
равен `Hole Size + 0,8 мм`. У сверлёных Pad задаются одинаковые X/Y-размеры меди
|
||||
на Top, Mid и Bottom, но исходная форма площадки сохраняется. Готовые размеры распознаются
|
||||
отдельно, поэтому скрипт можно запускать повторно без дальнейшего увеличения.
|
||||
|
||||
Скрипт проверяет активное общее правило Clearance со Scope `All / All`. Для
|
||||
каждой группы отдельное правило создаётся или обновляется только тогда, когда
|
||||
общий clearance меньше требуемого зазора от края меди. Если общий clearance уже
|
||||
достаточен, правило группы не создаётся, а ранее созданное скриптом правило
|
||||
удаляется.
|
||||
|
||||
Возможные отдельные правила:
|
||||
|
||||
- `RIVET_03_RoutingIsolation` — зазор от края меди `0,25 мм`;
|
||||
- `RIVET_11_RoutingIsolation` — зазор от края меди `0,30 мм`;
|
||||
- `RIVET_GT11_RoutingIsolation` — зазор от края меди `0,45 мм`.
|
||||
|
||||
Область действия первой стороны каждого правила автоматически выбирает Via и
|
||||
plated Pad с соответствующей парой Hole Size/диаметр. Вторая сторона выбирает
|
||||
только дорожки, дуги трассировки, медные Fill/Region и Polygon. Поэтому другие
|
||||
Pad и Via могут располагаться ближе и продолжают использовать обычное правило
|
||||
clearance платы, например `0,5 мм`.
|
||||
|
||||
Например, при общем clearance `0,5 мм` он уже превышает все три требуемых
|
||||
значения (`0,25 / 0,30 / 0,45 мм`), поэтому ни одно дополнительное правило не
|
||||
понадобится.
|
||||
|
||||
## Запуск
|
||||
|
||||
1. Откройте `RivetViaConverter.PrjScr` в Altium Designer.
|
||||
2. Откройте и сделайте активным нужный `PcbDoc`.
|
||||
3. Выберите `File -> Run Script`.
|
||||
4. Для проверки без изменений запустите `AnalyzeVias` — несмотря на историческое
|
||||
имя процедуры, она проверяет и Via, и сверлёные Pad.
|
||||
5. Для преобразования и синхронизации правил запустите `ConvertViasToRivets` —
|
||||
она изменяет оба типа объектов.
|
||||
6. Проверьте показанную сводку и нажмите `OK`.
|
||||
|
||||
Все изменения формируются как одна операция Undo. Скрипт не сохраняет файл
|
||||
автоматически.
|
||||
|
||||
## После преобразования
|
||||
|
||||
Запустите DRC: увеличенные площадки могут нарушить Clearance или выйти слишком
|
||||
близко к контуру платы. Также проверьте настройки Solder Mask Expansion: медь
|
||||
вокруг заклёпки должна быть открыта с обеих сторон платы. Сам скрипт настройки
|
||||
паяльной маски и правила PCB не меняет.
|
||||
8
PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr
Normal file
8
PCB/Scripts/RivetViaConverter/RivetViaConverter.PrjScr
Normal file
@@ -0,0 +1,8 @@
|
||||
[Design]
|
||||
Version=1.0
|
||||
HierarchyMode=0
|
||||
|
||||
[Document1]
|
||||
DocumentPath=RivetViaConverter.pas
|
||||
AnnotationEnabled=1
|
||||
|
||||
843
PCB/Scripts/RivetViaConverter/RivetViaConverter.pas
Normal file
843
PCB/Scripts/RivetViaConverter/RivetViaConverter.pas
Normal file
@@ -0,0 +1,843 @@
|
||||
{ RivetViaConverter.pas
|
||||
|
||||
Converts through-hole vias and drilled pads to the available PCB rivet sizes.
|
||||
Source classification is based on the object's current hole diameter:
|
||||
|
||||
<= 0.3 mm -> drill 0.9 mm, copper diameter 1.7 mm
|
||||
<= 1.1 mm -> drill 1.3 mm, copper diameter 2.1 mm
|
||||
> 1.1 mm -> drill 2.0 mm, copper diameter 2.8 mm
|
||||
|
||||
Copper diameter is always drill diameter + 0.8 mm.
|
||||
Existing complete hole/diameter target pairs are recognized before source
|
||||
thresholds, making repeated runs safe (idempotent).
|
||||
}
|
||||
|
||||
Const
|
||||
CLASS_NONE = 0;
|
||||
CLASS_03 = 1;
|
||||
CLASS_08 = 2;
|
||||
CLASS_12 = 3;
|
||||
CLASS_NOT_THROUGH = 4;
|
||||
|
||||
Var
|
||||
GTotalVias : Integer;
|
||||
GChange03 : Integer;
|
||||
GChange08 : Integer;
|
||||
GChange12 : Integer;
|
||||
GAlreadyReady : Integer;
|
||||
GSkippedLarge : Integer;
|
||||
GSkippedNotThru : Integer;
|
||||
GFootprintVias : Integer;
|
||||
GTotalDrilledPads : Integer;
|
||||
GFootprintPads : Integer;
|
||||
GSkippedNoRing : Integer;
|
||||
GSkippedNonPlated : Integer;
|
||||
GRulesCreated : Integer;
|
||||
GRulesUpdated : Integer;
|
||||
GRulesRemoved : Integer;
|
||||
GRulesNotNeeded : Integer;
|
||||
GDefaultClearance : TCoord;
|
||||
GDefaultRuleFound : Boolean;
|
||||
|
||||
Function CoordIsNear(A, B : TCoord) : Boolean;
|
||||
Begin
|
||||
Result := Abs(A - B) <= MMsToCoord(0.001);
|
||||
End;
|
||||
|
||||
Function IsThroughBoardVia(Via : IPCB_Via) : Boolean;
|
||||
Begin
|
||||
Result := (Via.LowLayer = eTopLayer) And
|
||||
(Via.HighLayer = eBottomLayer);
|
||||
End;
|
||||
|
||||
Function ViaHasNoAnnularRing(Via : IPCB_Via) : Boolean;
|
||||
Begin
|
||||
Result := Via.Size <= Via.HoleSize + MMsToCoord(0.001);
|
||||
End;
|
||||
|
||||
Function ClassifyHole(Hole : TCoord;
|
||||
Var TargetHole : TCoord;
|
||||
Var TargetSize : TCoord) : Integer;
|
||||
Begin
|
||||
Result := CLASS_NONE;
|
||||
TargetHole := 0;
|
||||
TargetSize := 0;
|
||||
|
||||
If Hole <= MMsToCoord(0.3) Then
|
||||
Begin
|
||||
Result := CLASS_03;
|
||||
TargetHole := MMsToCoord(0.9);
|
||||
TargetSize := MMsToCoord(1.7);
|
||||
End
|
||||
Else If Hole <= MMsToCoord(1.1) Then
|
||||
Begin
|
||||
Result := CLASS_08;
|
||||
TargetHole := MMsToCoord(1.3);
|
||||
TargetSize := MMsToCoord(2.1);
|
||||
End
|
||||
Else If Hole > MMsToCoord(1.1) Then
|
||||
Begin
|
||||
Result := CLASS_12;
|
||||
TargetHole := MMsToCoord(2.0);
|
||||
TargetSize := MMsToCoord(2.8);
|
||||
End;
|
||||
End;
|
||||
|
||||
Function ViaMatchesKnownTemplate(Via : IPCB_Via) : Boolean;
|
||||
Begin
|
||||
Result :=
|
||||
(CoordIsNear(Via.HoleSize, MMsToCoord(0.9)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(1.7))) Or
|
||||
(CoordIsNear(Via.HoleSize, MMsToCoord(1.3)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(2.1))) Or
|
||||
(CoordIsNear(Via.HoleSize, MMsToCoord(2.0)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(2.8)));
|
||||
End;
|
||||
|
||||
Function ClassifyVia(Via : IPCB_Via;
|
||||
Var TargetHole : TCoord;
|
||||
Var TargetSize : TCoord) : Integer;
|
||||
Begin
|
||||
If Not IsThroughBoardVia(Via) Then
|
||||
Begin
|
||||
TargetHole := 0;
|
||||
TargetSize := 0;
|
||||
Result := CLASS_NOT_THROUGH;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
{ Migrate exact target pairs produced by older script versions without
|
||||
moving them into a larger group based on their already enlarged drill. }
|
||||
If CoordIsNear(Via.HoleSize, MMsToCoord(0.9)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(2.0)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(0.9);
|
||||
TargetSize := MMsToCoord(1.7);
|
||||
Result := CLASS_03;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
If CoordIsNear(Via.HoleSize, MMsToCoord(1.3)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(2.5)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(1.3);
|
||||
TargetSize := MMsToCoord(2.1);
|
||||
Result := CLASS_08;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
If CoordIsNear(Via.HoleSize, MMsToCoord(2.0)) And
|
||||
CoordIsNear(Via.Size, MMsToCoord(3.5)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(2.0);
|
||||
TargetSize := MMsToCoord(2.8);
|
||||
Result := CLASS_12;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
Result := ClassifyHole(Via.HoleSize, TargetHole, TargetSize);
|
||||
End;
|
||||
|
||||
Procedure ResetCounts;
|
||||
Begin
|
||||
GTotalVias := 0;
|
||||
GChange03 := 0;
|
||||
GChange08 := 0;
|
||||
GChange12 := 0;
|
||||
GAlreadyReady := 0;
|
||||
GSkippedLarge := 0;
|
||||
GSkippedNotThru := 0;
|
||||
GFootprintVias := 0;
|
||||
GTotalDrilledPads := 0;
|
||||
GFootprintPads := 0;
|
||||
GSkippedNoRing := 0;
|
||||
GSkippedNonPlated := 0;
|
||||
End;
|
||||
|
||||
Procedure AnalyzeOneVia(Via : IPCB_Via; IsInFootprint : Boolean);
|
||||
Var
|
||||
ViaClass : Integer;
|
||||
TargetHole : TCoord;
|
||||
TargetSize : TCoord;
|
||||
Begin
|
||||
Inc(GTotalVias);
|
||||
If IsInFootprint Then
|
||||
Inc(GFootprintVias);
|
||||
|
||||
If ViaHasNoAnnularRing(Via) Then
|
||||
Begin
|
||||
Inc(GSkippedNoRing);
|
||||
Exit;
|
||||
End;
|
||||
|
||||
ViaClass := ClassifyVia(Via, TargetHole, TargetSize);
|
||||
|
||||
If ViaClass = CLASS_NOT_THROUGH Then
|
||||
Inc(GSkippedNotThru)
|
||||
Else If ViaClass = CLASS_NONE Then
|
||||
Inc(GSkippedLarge)
|
||||
Else If ViaMatchesKnownTemplate(Via) Then
|
||||
Inc(GAlreadyReady)
|
||||
Else If ViaClass = CLASS_03 Then
|
||||
Inc(GChange03)
|
||||
Else If ViaClass = CLASS_08 Then
|
||||
Inc(GChange08)
|
||||
Else If ViaClass = CLASS_12 Then
|
||||
Inc(GChange12);
|
||||
End;
|
||||
|
||||
Function PadHasNoAnnularRing(Pad : IPCB_Pad) : Boolean;
|
||||
Var
|
||||
MaxSize : TCoord;
|
||||
Begin
|
||||
MaxSize := Pad.TopXSize;
|
||||
If Pad.TopYSize > MaxSize Then MaxSize := Pad.TopYSize;
|
||||
If Pad.MidXSize > MaxSize Then MaxSize := Pad.MidXSize;
|
||||
If Pad.MidYSize > MaxSize Then MaxSize := Pad.MidYSize;
|
||||
If Pad.BotXSize > MaxSize Then MaxSize := Pad.BotXSize;
|
||||
If Pad.BotYSize > MaxSize Then MaxSize := Pad.BotYSize;
|
||||
|
||||
Result := MaxSize <= Pad.HoleSize + MMsToCoord(0.001);
|
||||
End;
|
||||
|
||||
Function PadIsReady(Pad : IPCB_Pad; TargetHole, TargetSize : TCoord) : Boolean;
|
||||
Begin
|
||||
Result := CoordIsNear(Pad.HoleSize, TargetHole) And
|
||||
CoordIsNear(Pad.TopXSize, TargetSize) And
|
||||
CoordIsNear(Pad.TopYSize, TargetSize) And
|
||||
CoordIsNear(Pad.MidXSize, TargetSize) And
|
||||
CoordIsNear(Pad.MidYSize, TargetSize) And
|
||||
CoordIsNear(Pad.BotXSize, TargetSize) And
|
||||
CoordIsNear(Pad.BotYSize, TargetSize);
|
||||
End;
|
||||
|
||||
Function PadMatchesKnownTemplate(Pad : IPCB_Pad) : Boolean;
|
||||
Begin
|
||||
Result :=
|
||||
PadIsReady(Pad, MMsToCoord(0.9), MMsToCoord(1.7)) Or
|
||||
PadIsReady(Pad, MMsToCoord(1.3), MMsToCoord(2.1)) Or
|
||||
PadIsReady(Pad, MMsToCoord(2.0), MMsToCoord(2.8));
|
||||
End;
|
||||
|
||||
Function ClassifyPad(Pad : IPCB_Pad;
|
||||
Var TargetHole : TCoord;
|
||||
Var TargetSize : TCoord) : Integer;
|
||||
Begin
|
||||
{ Migrate exact target pairs produced by older script versions. }
|
||||
If PadIsReady(Pad, MMsToCoord(0.9), MMsToCoord(2.0)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(0.9);
|
||||
TargetSize := MMsToCoord(1.7);
|
||||
Result := CLASS_03;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
If PadIsReady(Pad, MMsToCoord(1.3), MMsToCoord(2.5)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(1.3);
|
||||
TargetSize := MMsToCoord(2.1);
|
||||
Result := CLASS_08;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
If PadIsReady(Pad, MMsToCoord(2.0), MMsToCoord(3.5)) Then
|
||||
Begin
|
||||
TargetHole := MMsToCoord(2.0);
|
||||
TargetSize := MMsToCoord(2.8);
|
||||
Result := CLASS_12;
|
||||
Exit;
|
||||
End;
|
||||
|
||||
Result := ClassifyHole(Pad.HoleSize, TargetHole, TargetSize);
|
||||
End;
|
||||
|
||||
Procedure AnalyzeOnePad(Pad : IPCB_Pad; IsInFootprint : Boolean);
|
||||
Var
|
||||
PadClass : Integer;
|
||||
TargetHole : TCoord;
|
||||
TargetSize : TCoord;
|
||||
Begin
|
||||
{ HoleSize = 0 means an SMD pad. It must remain untouched. }
|
||||
If Pad.HoleSize <= 0 Then
|
||||
Exit;
|
||||
|
||||
Inc(GTotalDrilledPads);
|
||||
If IsInFootprint Then
|
||||
Inc(GFootprintPads);
|
||||
|
||||
If Not Pad.Plated Then
|
||||
Begin
|
||||
Inc(GSkippedNonPlated);
|
||||
Exit;
|
||||
End;
|
||||
|
||||
If PadHasNoAnnularRing(Pad) Then
|
||||
Begin
|
||||
Inc(GSkippedNoRing);
|
||||
Exit;
|
||||
End;
|
||||
|
||||
PadClass := ClassifyPad(Pad, TargetHole, TargetSize);
|
||||
|
||||
If PadClass = CLASS_NONE Then
|
||||
Inc(GSkippedLarge)
|
||||
Else If PadMatchesKnownTemplate(Pad) Then
|
||||
Inc(GAlreadyReady)
|
||||
Else If PadClass = CLASS_03 Then
|
||||
Inc(GChange03)
|
||||
Else If PadClass = CLASS_08 Then
|
||||
Inc(GChange08)
|
||||
Else If PadClass = CLASS_12 Then
|
||||
Inc(GChange12);
|
||||
End;
|
||||
|
||||
Procedure AnalyzeBoard(Board : IPCB_Board);
|
||||
Var
|
||||
Iterator : IPCB_BoardIterator;
|
||||
ComponentIterator : IPCB_BoardIterator;
|
||||
GroupIterator : IPCB_GroupIterator;
|
||||
Via : IPCB_Via;
|
||||
Pad : IPCB_Pad;
|
||||
Component : IPCB_Component;
|
||||
Begin
|
||||
ResetCounts;
|
||||
|
||||
{ First process free vias placed directly on the board. }
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(eViaObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Via := Iterator.FirstPCBObject;
|
||||
While Via <> Nil Do
|
||||
Begin
|
||||
If Not Via.InComponent Then
|
||||
AnalyzeOneVia(Via, False);
|
||||
Via := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
|
||||
{ Process free drilled pads placed directly on the board. }
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(ePadObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Pad := Iterator.FirstPCBObject;
|
||||
While Pad <> Nil Do
|
||||
Begin
|
||||
If Not Pad.InComponent Then
|
||||
AnalyzeOnePad(Pad, False);
|
||||
Pad := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
|
||||
{ Board iterators do not expose component child primitives consistently
|
||||
across Altium versions. Walk each footprint explicitly. }
|
||||
ComponentIterator := Board.BoardIterator_Create;
|
||||
ComponentIterator.AddFilter_ObjectSet(MkSet(eComponentObject));
|
||||
ComponentIterator.AddFilter_LayerSet(AllLayers);
|
||||
ComponentIterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Component := ComponentIterator.FirstPCBObject;
|
||||
While Component <> Nil Do
|
||||
Begin
|
||||
GroupIterator := Component.GroupIterator_Create;
|
||||
GroupIterator.AddFilter_ObjectSet(MkSet(eViaObject));
|
||||
Try
|
||||
Via := GroupIterator.FirstPCBObject;
|
||||
While Via <> Nil Do
|
||||
Begin
|
||||
AnalyzeOneVia(Via, True);
|
||||
Via := GroupIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Component.GroupIterator_Destroy(GroupIterator);
|
||||
End;
|
||||
|
||||
GroupIterator := Component.GroupIterator_Create;
|
||||
GroupIterator.AddFilter_ObjectSet(MkSet(ePadObject));
|
||||
Try
|
||||
Pad := GroupIterator.FirstPCBObject;
|
||||
While Pad <> Nil Do
|
||||
Begin
|
||||
AnalyzeOnePad(Pad, True);
|
||||
Pad := GroupIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Component.GroupIterator_Destroy(GroupIterator);
|
||||
End;
|
||||
|
||||
Component := ComponentIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(ComponentIterator);
|
||||
End;
|
||||
End;
|
||||
|
||||
Function ChangesCount : Integer;
|
||||
Begin
|
||||
Result := GChange03 + GChange08 + GChange12;
|
||||
End;
|
||||
|
||||
Function BuildSummary : String;
|
||||
Begin
|
||||
Result :=
|
||||
'Rivet hole analysis' + #13#10 + #13#10 +
|
||||
'All vias: ' + IntToStr(GTotalVias) + #13#10 +
|
||||
'Vias inside footprints: ' + IntToStr(GFootprintVias) + #13#10 +
|
||||
'All drilled pads: ' + IntToStr(GTotalDrilledPads) + #13#10 +
|
||||
'Drilled pads inside footprints: ' + IntToStr(GFootprintPads) + #13#10 +
|
||||
'Will set 1.7 / 0.9 mm: ' + IntToStr(GChange03) + #13#10 +
|
||||
'Will set 2.1 / 1.3 mm: ' + IntToStr(GChange08) + #13#10 +
|
||||
'Will set 2.8 / 2.0 mm: ' + IntToStr(GChange12) + #13#10 +
|
||||
'Already ready: ' + IntToStr(GAlreadyReady) + #13#10 +
|
||||
'Skipped, no annular ring: ' + IntToStr(GSkippedNoRing) + #13#10 +
|
||||
'Skipped, non-plated pad: ' + IntToStr(GSkippedNonPlated) + #13#10 +
|
||||
'Skipped, unsupported hole: ' + IntToStr(GSkippedLarge) + #13#10 +
|
||||
'Skipped, blind/buried via: ' + IntToStr(GSkippedNotThru);
|
||||
End;
|
||||
|
||||
Function ApplyOneVia(Via : IPCB_Via) : Boolean;
|
||||
Var
|
||||
ViaClass : Integer;
|
||||
TargetHole : TCoord;
|
||||
TargetSize : TCoord;
|
||||
Begin
|
||||
Result := False;
|
||||
If ViaHasNoAnnularRing(Via) Then
|
||||
Exit;
|
||||
|
||||
If ViaMatchesKnownTemplate(Via) Then
|
||||
Exit;
|
||||
|
||||
ViaClass := ClassifyVia(Via, TargetHole, TargetSize);
|
||||
|
||||
If (ViaClass >= CLASS_03) And (ViaClass <= CLASS_12) And
|
||||
(Not (CoordIsNear(Via.HoleSize, TargetHole) And
|
||||
CoordIsNear(Via.Size, TargetSize))) Then
|
||||
Begin
|
||||
PCBServer.SendMessageToRobots(
|
||||
Via.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_BeginModify,
|
||||
c_NoEventData);
|
||||
Try
|
||||
Via.HoleSize := TargetHole;
|
||||
Via.Size := TargetSize;
|
||||
Finally
|
||||
PCBServer.SendMessageToRobots(
|
||||
Via.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_EndModify,
|
||||
c_NoEventData);
|
||||
End;
|
||||
Result := True;
|
||||
End;
|
||||
End;
|
||||
|
||||
Function ApplyOnePad(Pad : IPCB_Pad) : Boolean;
|
||||
Var
|
||||
PadClass : Integer;
|
||||
TargetHole : TCoord;
|
||||
TargetSize : TCoord;
|
||||
Begin
|
||||
Result := False;
|
||||
If Pad.HoleSize <= 0 Then
|
||||
Exit;
|
||||
|
||||
If Not Pad.Plated Then
|
||||
Exit;
|
||||
|
||||
If PadHasNoAnnularRing(Pad) Then
|
||||
Exit;
|
||||
|
||||
If PadMatchesKnownTemplate(Pad) Then
|
||||
Exit;
|
||||
|
||||
PadClass := ClassifyPad(Pad, TargetHole, TargetSize);
|
||||
If (PadClass >= CLASS_03) And (PadClass <= CLASS_12) And
|
||||
(Not PadIsReady(Pad, TargetHole, TargetSize)) Then
|
||||
Begin
|
||||
PCBServer.SendMessageToRobots(
|
||||
Pad.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_BeginModify,
|
||||
c_NoEventData);
|
||||
Try
|
||||
Pad.HoleSize := TargetHole;
|
||||
Pad.TopXSize := TargetSize;
|
||||
Pad.TopYSize := TargetSize;
|
||||
Pad.MidXSize := TargetSize;
|
||||
Pad.MidYSize := TargetSize;
|
||||
Pad.BotXSize := TargetSize;
|
||||
Pad.BotYSize := TargetSize;
|
||||
Finally
|
||||
PCBServer.SendMessageToRobots(
|
||||
Pad.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_EndModify,
|
||||
c_NoEventData);
|
||||
End;
|
||||
Result := True;
|
||||
End;
|
||||
End;
|
||||
|
||||
Function FindIsolationRule(Board : IPCB_Board;
|
||||
RuleName : String) : IPCB_ClearanceConstraint;
|
||||
Var
|
||||
Iterator : IPCB_BoardIterator;
|
||||
Rule : IPCB_Rule;
|
||||
Begin
|
||||
Result := Nil;
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(eRuleObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Rule := Iterator.FirstPCBObject;
|
||||
While Rule <> Nil Do
|
||||
Begin
|
||||
If (Rule.RuleKind = eRule_Clearance) And
|
||||
(Rule.Name = RuleName) Then
|
||||
Begin
|
||||
Result := Rule;
|
||||
Exit;
|
||||
End;
|
||||
Rule := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
End;
|
||||
|
||||
Function ScopeIsAll(ScopeExpression : String) : Boolean;
|
||||
Var
|
||||
S : String;
|
||||
Begin
|
||||
S := UpperCase(Trim(ScopeExpression));
|
||||
Result := (S = 'ALL') Or (S = '');
|
||||
End;
|
||||
|
||||
Function FindDefaultClearance(Board : IPCB_Board;
|
||||
Var DefaultGap : TCoord) : Boolean;
|
||||
Var
|
||||
Iterator : IPCB_BoardIterator;
|
||||
Rule : IPCB_Rule;
|
||||
ClearanceRule : IPCB_ClearanceConstraint;
|
||||
Begin
|
||||
Result := False;
|
||||
DefaultGap := 0;
|
||||
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(eRuleObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
{ Rule iterators follow the board's rule order. The first enabled
|
||||
All/All clearance rule is therefore the active default rule. }
|
||||
Rule := Iterator.FirstPCBObject;
|
||||
While Rule <> Nil Do
|
||||
Begin
|
||||
If (Rule.RuleKind = eRule_Clearance) And
|
||||
Rule.DRCEnabled And
|
||||
ScopeIsAll(Rule.Scope1Expression) And
|
||||
ScopeIsAll(Rule.Scope2Expression) Then
|
||||
Begin
|
||||
ClearanceRule := Rule;
|
||||
DefaultGap := ClearanceRule.Gap;
|
||||
Result := True;
|
||||
Exit;
|
||||
End;
|
||||
Rule := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
End;
|
||||
|
||||
Procedure RemoveIsolationRuleIfPresent(Board : IPCB_Board;
|
||||
RuleName : String);
|
||||
Var
|
||||
Rule : IPCB_ClearanceConstraint;
|
||||
Begin
|
||||
Rule := FindIsolationRule(Board, RuleName);
|
||||
If Rule <> Nil Then
|
||||
Begin
|
||||
Board.RemovePCBObject(Rule);
|
||||
Inc(GRulesRemoved);
|
||||
End;
|
||||
End;
|
||||
|
||||
Function BuildIsolationScope(HoleMM, CopperMM : String) : String;
|
||||
Begin
|
||||
Result :=
|
||||
'((IsVia And (AsMM(HoleSize) = ' + HoleMM + ') And ' +
|
||||
'(AsMM(ViaDiameter) = ' + CopperMM + ')) Or ' +
|
||||
'(IsPad And (PadIsPlated = ''True'') And ' +
|
||||
'(AsMM(HoleSize) = ' + HoleMM + ') And ' +
|
||||
'(AsMM(PadXSize_TopLayer) = ' + CopperMM + ') And ' +
|
||||
'(AsMM(PadYSize_TopLayer) = ' + CopperMM + ')))';
|
||||
End;
|
||||
|
||||
Procedure EnsureIsolationRule(Board : IPCB_Board;
|
||||
RuleName : String;
|
||||
HoleMM : String;
|
||||
CopperMM : String;
|
||||
IsolationGap : TCoord;
|
||||
DefaultFound : Boolean;
|
||||
DefaultGap : TCoord);
|
||||
Var
|
||||
Rule : IPCB_ClearanceConstraint;
|
||||
Begin
|
||||
If DefaultFound And (DefaultGap >= IsolationGap) Then
|
||||
Begin
|
||||
RemoveIsolationRuleIfPresent(Board, RuleName);
|
||||
Inc(GRulesNotNeeded);
|
||||
Exit;
|
||||
End;
|
||||
|
||||
Rule := FindIsolationRule(Board, RuleName);
|
||||
|
||||
If Rule = Nil Then
|
||||
Begin
|
||||
Rule := PCBServer.PCBRuleFactory(eRule_Clearance);
|
||||
PCBServer.SendMessageToRobots(
|
||||
Rule.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_BeginModify,
|
||||
c_NoEventData);
|
||||
Try
|
||||
Rule.Name := RuleName;
|
||||
Rule.Comment :=
|
||||
'Rivet routing/polygon isolation. Pads and vias use normal clearance.';
|
||||
Rule.DRCEnabled := True;
|
||||
Rule.Scope1Expression := BuildIsolationScope(HoleMM, CopperMM);
|
||||
Rule.Scope2Expression :=
|
||||
'IsTrack Or IsArc Or IsFill Or IsRegion Or InPolygon';
|
||||
Rule.Gap := IsolationGap;
|
||||
Board.AddPCBObject(Rule);
|
||||
Finally
|
||||
PCBServer.SendMessageToRobots(
|
||||
Rule.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_EndModify,
|
||||
c_NoEventData);
|
||||
End;
|
||||
Inc(GRulesCreated);
|
||||
End
|
||||
Else
|
||||
Begin
|
||||
PCBServer.SendMessageToRobots(
|
||||
Rule.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_BeginModify,
|
||||
c_NoEventData);
|
||||
Try
|
||||
Rule.Comment :=
|
||||
'Rivet routing/polygon isolation. Pads and vias use normal clearance.';
|
||||
Rule.DRCEnabled := True;
|
||||
Rule.Scope1Expression := BuildIsolationScope(HoleMM, CopperMM);
|
||||
Rule.Scope2Expression :=
|
||||
'IsTrack Or IsArc Or IsFill Or IsRegion Or InPolygon';
|
||||
Rule.Gap := IsolationGap;
|
||||
Finally
|
||||
PCBServer.SendMessageToRobots(
|
||||
Rule.I_ObjectAddress,
|
||||
c_Broadcast,
|
||||
PCBM_EndModify,
|
||||
c_NoEventData);
|
||||
End;
|
||||
Inc(GRulesUpdated);
|
||||
End;
|
||||
End;
|
||||
|
||||
Procedure EnsureIsolationRules(Board : IPCB_Board);
|
||||
Begin
|
||||
GRulesCreated := 0;
|
||||
GRulesUpdated := 0;
|
||||
GRulesRemoved := 0;
|
||||
GRulesNotNeeded := 0;
|
||||
GDefaultRuleFound := FindDefaultClearance(Board, GDefaultClearance);
|
||||
|
||||
{ Overall isolation diameters are 2.2, 2.7 and 3.7 mm.
|
||||
Gap is measured from the copper edge, hence (zone - copper) / 2. }
|
||||
EnsureIsolationRule(Board, 'RIVET_03_RoutingIsolation',
|
||||
'0.9', '1.7', MMsToCoord(0.25),
|
||||
GDefaultRuleFound, GDefaultClearance);
|
||||
EnsureIsolationRule(Board, 'RIVET_11_RoutingIsolation',
|
||||
'1.3', '2.1', MMsToCoord(0.30),
|
||||
GDefaultRuleFound, GDefaultClearance);
|
||||
EnsureIsolationRule(Board, 'RIVET_GT11_RoutingIsolation',
|
||||
'2.0', '2.8', MMsToCoord(0.45),
|
||||
GDefaultRuleFound, GDefaultClearance);
|
||||
End;
|
||||
|
||||
Function ApplyChanges(Board : IPCB_Board) : Integer;
|
||||
Var
|
||||
Iterator : IPCB_BoardIterator;
|
||||
ComponentIterator : IPCB_BoardIterator;
|
||||
GroupIterator : IPCB_GroupIterator;
|
||||
Via : IPCB_Via;
|
||||
Pad : IPCB_Pad;
|
||||
Component : IPCB_Component;
|
||||
Begin
|
||||
Result := 0;
|
||||
PCBServer.PreProcess;
|
||||
Try
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(eViaObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Via := Iterator.FirstPCBObject;
|
||||
While Via <> Nil Do
|
||||
Begin
|
||||
If (Not Via.InComponent) And ApplyOneVia(Via) Then
|
||||
Inc(Result);
|
||||
Via := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
|
||||
{ Process free drilled pads. }
|
||||
Iterator := Board.BoardIterator_Create;
|
||||
Iterator.AddFilter_ObjectSet(MkSet(ePadObject));
|
||||
Iterator.AddFilter_LayerSet(AllLayers);
|
||||
Iterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Pad := Iterator.FirstPCBObject;
|
||||
While Pad <> Nil Do
|
||||
Begin
|
||||
If (Not Pad.InComponent) And ApplyOnePad(Pad) Then
|
||||
Inc(Result);
|
||||
Pad := Iterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(Iterator);
|
||||
End;
|
||||
|
||||
{ Process vias and drilled pads embedded in placed footprints. }
|
||||
ComponentIterator := Board.BoardIterator_Create;
|
||||
ComponentIterator.AddFilter_ObjectSet(MkSet(eComponentObject));
|
||||
ComponentIterator.AddFilter_LayerSet(AllLayers);
|
||||
ComponentIterator.AddFilter_Method(eProcessAll);
|
||||
|
||||
Try
|
||||
Component := ComponentIterator.FirstPCBObject;
|
||||
While Component <> Nil Do
|
||||
Begin
|
||||
GroupIterator := Component.GroupIterator_Create;
|
||||
GroupIterator.AddFilter_ObjectSet(MkSet(eViaObject));
|
||||
Try
|
||||
Via := GroupIterator.FirstPCBObject;
|
||||
While Via <> Nil Do
|
||||
Begin
|
||||
If ApplyOneVia(Via) Then
|
||||
Inc(Result);
|
||||
Via := GroupIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Component.GroupIterator_Destroy(GroupIterator);
|
||||
End;
|
||||
|
||||
GroupIterator := Component.GroupIterator_Create;
|
||||
GroupIterator.AddFilter_ObjectSet(MkSet(ePadObject));
|
||||
Try
|
||||
Pad := GroupIterator.FirstPCBObject;
|
||||
While Pad <> Nil Do
|
||||
Begin
|
||||
If ApplyOnePad(Pad) Then
|
||||
Inc(Result);
|
||||
Pad := GroupIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Component.GroupIterator_Destroy(GroupIterator);
|
||||
End;
|
||||
|
||||
Component := ComponentIterator.NextPCBObject;
|
||||
End;
|
||||
Finally
|
||||
Board.BoardIterator_Destroy(ComponentIterator);
|
||||
End;
|
||||
|
||||
EnsureIsolationRules(Board);
|
||||
Finally
|
||||
PCBServer.PostProcess;
|
||||
End;
|
||||
|
||||
Client.SendMessage('PCB:Zoom', 'Action=Redraw', 255, Client.CurrentView);
|
||||
End;
|
||||
|
||||
Procedure AnalyzeVias;
|
||||
Var
|
||||
Board : IPCB_Board;
|
||||
Begin
|
||||
Board := PCBServer.GetCurrentPCBBoard;
|
||||
If Board = Nil Then
|
||||
Begin
|
||||
ShowMessage('Open and focus a PCB document first.');
|
||||
Exit;
|
||||
End;
|
||||
|
||||
AnalyzeBoard(Board);
|
||||
ShowMessage(BuildSummary);
|
||||
End;
|
||||
|
||||
Procedure ConvertViasToRivets;
|
||||
Var
|
||||
Board : IPCB_Board;
|
||||
ChangedCount : Integer;
|
||||
Prompt : String;
|
||||
ResultMessage : String;
|
||||
Begin
|
||||
Board := PCBServer.GetCurrentPCBBoard;
|
||||
If Board = Nil Then
|
||||
Begin
|
||||
ShowMessage('Open and focus a PCB document first.');
|
||||
Exit;
|
||||
End;
|
||||
|
||||
AnalyzeBoard(Board);
|
||||
Prompt := BuildSummary + #13#10 + #13#10;
|
||||
If ChangesCount = 0 Then
|
||||
Prompt := Prompt + 'Object sizes are ready. Isolation rules will still be reconciled.' +
|
||||
#13#10;
|
||||
Prompt := Prompt +
|
||||
'Press OK to modify the board and reconcile isolation rules,' +
|
||||
#13#10 + 'or Cancel to stop.';
|
||||
If MessageDlg(Prompt, mtConfirmation, mbOKCancel, 0) = mrCancel Then
|
||||
Exit;
|
||||
|
||||
ChangedCount := ApplyChanges(Board);
|
||||
ResultMessage :=
|
||||
'Done. Modified vias/pads: ' + IntToStr(ChangedCount) +
|
||||
#13#10 + 'Isolation rules created: ' + IntToStr(GRulesCreated) +
|
||||
#13#10 + 'Isolation rules updated: ' + IntToStr(GRulesUpdated) +
|
||||
#13#10 + 'Isolation rules removed: ' + IntToStr(GRulesRemoved) +
|
||||
#13#10 + 'Groups covered by default clearance: ' +
|
||||
IntToStr(GRulesNotNeeded);
|
||||
|
||||
If GDefaultRuleFound Then
|
||||
ResultMessage := ResultMessage + #13#10 +
|
||||
'Default clearance: ' + FloatToStr(CoordToMMs(GDefaultClearance)) + ' mm'
|
||||
Else
|
||||
ResultMessage := ResultMessage + #13#10 +
|
||||
'Default All/All clearance rule was not found.';
|
||||
|
||||
ResultMessage := ResultMessage + #13#10 +
|
||||
'Use Undo to revert the complete operation.';
|
||||
ShowMessage(ResultMessage);
|
||||
End;
|
||||
Reference in New Issue
Block a user