#usage "en: PCB Quote Service"
"
"
"Get a quote for manufacturing your PCB.
",
"de: PCB-Angebots-Service\n"
"
"
"Hiermit gelangen Sie zu einem Angebot zur Fertigung Ihres Boards.
"
"Autor: support@cadsoft.de
"
// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED
#require 5.1000
int Version = 25; // Refer to versioning system. Needed because ULP version independent from EAGLE version
enum { false = 0, true = 1 };
// Debug stuff
int Dbg = 0;
string DbgFile;
string DbgMsg;
// Messages
string Warnings;
int Useless; // When a quote doesn't make sense (e.g empty board or such)
// Design rules
int Export = 1;
string DruFile;
// Units / scaling: Handling change of internal units V5 -> V6:
int UFactor = (EAGLE_VERSION < 6 && EAGLE_RELEASE < 12) ? 1 : 32;
int PrefUnit; // Preferred unit (representation in dialog)
// Fabrication parameters
string BoardName;
int NrLayers;
int CopperThicknessOutside = INT_MAX;
int CopperThicknessInside = INT_MAX;
int MaterialThickness;
int MinTraceWidth = INT_MAX;
int MinHoleSize = INT_MAX;
string SolderSides;
string SilkScreenSides;
int NrBlindBuriedTypes;
int MinPitch = INT_MAX;
int NrSmdPadsTop;
int NrSmdPadsBottom;
// Assembly parameters
int NrPackages;
int NrThruHoles;
int NrBGAs;
int NrQFNs;
int NrFinePitchs;
int NrOtherSMDs;
int HasSMDBothSides;
// For computing width and height
real XMin = REAL_MAX, XMax = -REAL_MAX, YMin = REAL_MAX, YMax = -REAL_MAX;
// Language support for dialogs: German/English
// Please keep to alphabetic sorting for better maintainability !
string Dictionary[] = {
"en\v"
"de\v",
"Assembly Parameters:\v"
"Bestückungs-Parameter:\v",
"Board length (dimension Y):\v"
"Boardlänge (Y-Richtung):\v",
"Board name:\v"
"Boardname:\v",
"Board thickness:\v"
"Boarddicke:\v",
"Board width (dimension X):\v"
"Boardbreite (X-Richtung):\v",
"Board width and length could not be determined from dimension layer. \v"
"Boardbreite und -länge konnten nicht bestimmt werden anhand des Dimension-Layers. \v",
"Both Sides\v"
"beidseitig\v",
"Bottom Side\v"
"unten\v",
"Please use the dimension layer to design the board outline.\v"
"Bitte benutzen Sie den Dimension-Layer, um die Boardumrisse festzulegen.\v",
"Calculating quote parameters...\v"
"Berechne Angebots-Parameter...\v",
"Change Country:\v"
"Land wechseln:\v",
"Close\v"
"Schliessen\v",
"+Continue...\v"
"+Weiter...\v",
"Copper thickness outer layers:\v"
"Kupferstärke aussen:\v",
"Copper thickness inner layers:\v"
"Kupferstärke innen:\v",
"Estimate based on outer layers failed. \v"
"Schätzung basierend auf Aussen-Layer fehlgeschlagen. \v",
"Estimation based on outer layers. \v"
"Schätzung basierend auf Aussen-Layer. \v",
"Fabrication Parameters:\v"
"Fertigungs-Parameter:\v",
"Get PCB Quote\v"
"Zum PCB-Angebot\v",
"A quote for this board doesn't make sense yet !\v"
"Ein Angebot für dieses Board macht noch keinen Sinn !\v",
"If you want to get a quote anyway, please follow the link.\v"
"Wenn Sie dennoch ein Angebot erhalten wollen, folgen Sie bitte dem Link.\v",
"Information:\v"
"Hinweise:\v",
"Minimum hole size:\v"
"Kleinster Bohrdurchmesser:\v",
"Minimum SMD pitch:\v"
"Kleinster SMD-Abstand:\v",
"Minimum trace width (track width):\v"
"Kleinste Bahnbreite:\v",
"Minimum trace width could not be determined.\v"
"Minimale Bahnbreite konnte nicht bestimmt werden.\v",
"No copper found in the signal layers. \v"
"Kein Kupfer in den Signal-Layern vorhanden. \v",
"No\v"
"Nein\v",
"None\v"
"keine\v",
"Number of\v"
"Anzahl von\v",
"Number of blind or buried hole types:\v"
"Anzahl von Blind- oder Buried-Bohrungsarten:\v",
"Number of different packages:\v"
"Anzahl verschiedener Packages:\v",
"Number of fine pitch packages:\v"
"Anzahl von Fine-Pitch-Packages:\v",
"Number of other SMDs:\v"
"Anzahl sonstiger SMDs:\v",
"Number of SMD pads on bottom:\v"
"Anzahl von SMD-Pads unten:\v",
"Number of SMD pads on top:\v"
"Anzahl von SMD-Pads oben:\v",
"Number of thru hole packages: \v"
"Anzahl von durchkontaktierten Packages: \v",
"Number of layers:\v"
"Anzahl Layer:\v",
"Wire(s) and/or polygon(s) with zero width found on signal layer(s). \v"
"Bahnen und/oder Polygone mit Breite 0 in Signal-Layer(n) gefunden. \v",
"None of the signal layers are used.\v"
"Keines der Signal-Layer wird verwendet.\v",
"Parameters:\v"
"Parameter:\v",
"PCB Quote Service\v"
"PCB-Angebots-Service\v",
"Please start from Layout editor !\v"
"Bitte starten Sie vom Layout-Editor aus !\v",
"Silkscreen sides:\v"
"Bestückungsdruck-Seiten:\v",
"SMDs on both sides:\v"
"SMDs beidseitig:\v",
"Solder sides:\v"
"Lötseiten:\v",
"There are still airwires left.\v"
"Es sind noch Luftlinien vorhanden.\v",
"Top Side\v"
"oben\v",
"undefined\v"
"unbestimmt\v",
"Warnings:\v"
"Warnungen:\v",
"Yes\v"
"Ja\v",
"Your board layout is incomplete:\v"
"Ihr Board-Layout ist unvollständig:\v"
};
string DlgLang = language();
if (DlgLang != "de") DlgLang = "en";
int LangIdx = strstr(Dictionary[0], DlgLang) / 3;
// Translate, based on dictionary
string tr(string s) {
string t = lookup(Dictionary, s, LangIdx, '\v');
return t ? t : s;
}
string Unknown = tr("undefined");
//- Auxiliary functions -------------------------------------------------------
string GetTargetURL() {
return "http://www.element14.com/community/community/knode/pcb_services";
}
// String handling
string I2Str(int i) { string str; sprintf(str, "%d", i); return str; }
string B2Str(int b) { return b ? "true" : "false"; }
// HTML stuff
string LItem(string txt) { return "
" + txt + ""; }
string Bold(string txt) { return "" + txt + ""; }
// Unit handling
string U2MmStr(int i) { string str; sprintf(str, "%f", u2mm(i)); return str; }
string U2InStr(int i) { string str; sprintf(str, "%f", u2inch(i)); return str; }
string U2OzStr(int i) { string str; sprintf(str, "%f", i/(350.0 * UFactor)); return str; }// Todo: Check this !
// Unit to preferred unit as String with extension !
string U2PrefUStrExt(int i) { return (PrefUnit == GRID_UNIT_MM) ? U2MmStr(i) + " mm" : U2InStr(i) + " in"; }
int Str2U(string s) {
if (strstr(s, "mm") > 0) return int(strtod(s) * 10000 * UFactor);
if (strstr(s, "mil") > 0) return int(strtod(s) * 10 * UFactor * 25.4);
if (strstr(s, "in") > 0) return int(strtod(s) * 10000 * UFactor * 25.4);
if (strstr(s, "mic") > 0) return int(strtod(s) * 10 * UFactor);
dlgMessageBox("Unknown unit in design rules !"); exit(EXIT_FAILURE);
return 0;
}
//-------------------------------------------------------
// Get PCB parameters
//-------------------------------------------------------
// Means really within, not at the borders
int Within(int val, int l, int u) { return (val > l) && (val < u); }
int ValidBBox() {
return (XMin < REAL_MAX) && (XMax > -REAL_MAX) &&
(YMin < REAL_MAX) && (YMax > -REAL_MAX) &&
(XMax - XMin > 1000) && (YMax - YMin > 1000); // Could make this much bigger, but that's not crucial (currently 0.1 mm)
}
/*
int CheckIfBGA(UL_PACKAGE LBRPAC) {
// board(B) B.libraries(L) L.packages(PAC)
// if (PAC.name == pacName) {
int i;
LBRPAC.contacts(C) {
LbrPacConXs[i] = C.x;
LbrPacConYs[i++] = C.y;
// Get grid dimensions
ConXMin = min(ConXMin, C.x);
conXMax = max(conXMax, C.x);
ConYMin = min(ConYMin, C.y);
conYMax = max(conYMax, C.y);
if (Dbg) { sprintf(DbgMsg, "LbrPac: (%f %f)\n", u2mm(LbrPacConXs[i-1]), u2mm(LbrPacConYs[i-1])); DbgTxt += DbgMsg; }
if (C.smd) {
ConDiam = C.smd.dx;
// All dx/dy must be equal
conDiamMin = min(conDiamMin, C.smd.dx);
conDiamMin = min(conDiamMin, C.smd.dy);
conDiamMax = max(conDiamMax, C.smd.dx);
conDiamMax = max(conDiamMax, C.smd.dy);
if (C.smd.roundness - 100 > roundTol) errRound = true;
}
else {
errType = true;
break;
}
}
}
}*/
// Update bounding box with another box
void UpdateBBoxBox(int xmin, int ymin, int xmax, int ymax) {
XMin = min(XMin, xmin);
XMax = max(XMax, xmax);
YMin = min(YMin, ymin);
YMax = max(YMax, ymax);
}
void UpdateBBoxWire(UL_WIRE W, int layer) {
if (W.layer == layer) {
real w2 = W.width/2;
real xmin = min(W.x1 - w2, W.x2 - w2), ymin = min(W.y1 - w2, W.y2 - w2);
real xmax = max(W.x1 + w2, W.x2 + w2), ymax = max(W.y1 + w2, W.y2 + w2);
if (W.arc) {
if (W.arc.angle2 > 360)
xmax = W.arc.xc + W.arc.radius + w2;
if (((W.arc.angle1 < 90) && (W.arc.angle2 > 90)) || (W.arc.angle2 > 450))
ymax = W.arc.yc + W.arc.radius + w2;
if (((W.arc.angle1 < 180) && (W.arc.angle2 > 180)) || (W.arc.angle2 > 540))
xmin = W.arc.xc - W.arc.radius - w2;
if (((W.arc.angle1 < 270) && (W.arc.angle2 > 270)) || (W.arc.angle2 > 630))
ymin = W.arc.yc - W.arc.radius - w2;
}
UpdateBBoxBox(xmin, ymin, xmax, ymax);
}
}
void UpdateBBoxCircle(UL_CIRCLE C, int layer) {
if (C.layer == layer) {
real w2 = C.width / 2;
UpdateBBoxBox(C.x - C.radius - w2, C.y - C.radius - w2, C.x + C.radius + w2, C.y + C.radius + w2);
}
}
void UpdateBBox(int layer, int el_origin) {
board(B) {
B.wires(W) UpdateBBoxWire(W, layer);
B.circles(C) UpdateBBoxCircle(C, layer);
B.elements(E) {
if (el_origin) UpdateBBoxBox(E.x, E.y, E.x, E.y); // Elements are always on signal layers.
E.package.wires(W) UpdateBBoxWire(W, layer);
E.package.circles(C) UpdateBBoxCircle(C, layer);
}
}
}
void GetDruParams() {
string lines[];
int nrlines = fileread(lines, DruFile);
if (nrlines == 0) dlgMessageBox("Error reading design rules !");
int layer_used[], nrlayers, bottom_idx;
for (int i = 0; i < nrlines; ++i) {
string words[];
int nrwords = strsplit(words, lines[i], ' ');
if (nrwords < 3) continue; // Empty line or some other stuff
string keyword = words[0];
if (keyword == "layerSetup")
for (int k = 1; k <= 16; ++k) {
string nr_pattern;
sprintf(nr_pattern, "[\\D]%d[\\D]", k); // Number, surrounded by 'no number character'
if (strxstr(words[2], nr_pattern) != -1) {
layer_used[k - 1] = true;
++NrLayers;
bottom_idx = k - 1;
}
}
else if (keyword == "mtCopper") {
if (nrwords != 18) dlgMessageBox("Unexpected design rules !");
int top_thickness = -1, bot_thickness;
for (int k = 2; k < nrwords; ++k) {
if (layer_used[k - 2]) {
int thickness = Str2U(words[k]);
if (top_thickness == -1)
top_thickness = thickness;
else if (k - 2 == bottom_idx)
bot_thickness = thickness;
else
CopperThicknessInside = min(thickness, CopperThicknessInside);
MaterialThickness += thickness;
}
}
CopperThicknessOutside = min(top_thickness, bot_thickness);
}
else if (keyword == "mtIsolate") {
if (nrwords != 17) dlgMessageBox("Unexpected design rules !");
// n used layers => n-1 isolate layers. If only 1 layer, also 1 isolate layer.
for (int k = 2; k < nrwords; ++k)
if (layer_used[k - 2])
MaterialThickness += Str2U(words[k]);
}
else if (keyword == "mdSmdSmd")
MinPitch = Str2U(words[2]);
}
}
void CalcMinTraceWidth() {
board(B) {
B.wires(W) if (W.layer <= 16) MinTraceWidth = min(W.width, MinTraceWidth);
B.signals(S) {
S.polygons(P) if (P.layer <= 16) MinTraceWidth = min(P.width, MinTraceWidth);
S.wires(W) if (W.layer <= 16) MinTraceWidth = min(W.width, MinTraceWidth);
}
}
}
void CheckHoles() {
int idx[], start[], end[], k;
board(B) {
B.holes(H) MinHoleSize = min(MinHoleSize, H.drill);
B.signals(S) S.vias(V) {
MinHoleSize = min(MinHoleSize, V.drill);
// Collect blind/buried holes !
if ((V.start > 1) || (V.end < 16)) {
start[k] = V.start;
end[k] = V.end;
++k;
}
}
B.elements(E) {
E.package.contacts(C) if (C.pad)
MinHoleSize = min(MinHoleSize, C.pad.drill);
E.package.holes(H) MinHoleSize = min(MinHoleSize, H.drill);
}
}
// NrBlindBuriedTypes: Find out how many different ones exist !
sort(k, idx, start, end);
for (int l = 0, cur_s = 0, cur_e = 0; l < k; ++l)
if ((start[idx[l]] != cur_s) || (end[idx[l]] != cur_e)) {
++NrBlindBuriedTypes;
cur_s = start[idx[l]];
cur_e = end[idx[l]];
}
}
void GetPCBParams() {
// Check out layers:
int smask_top, smask_bottom, silk_top, silk_bottom;
int nr_used_layers;
board(B) {
BoardName = filename(B.name);
B.layers(L) if (L.used)
if (L.number <= 16) ++nr_used_layers;
else if (L.number == LAYER_TSTOP) smask_top = 1;
else if (L.number == LAYER_BSTOP) smask_bottom = 1;
else if (L.number == LAYER_TPLACE) silk_top = 1; // Could also take TNAMES/TVALUES. Leave the criterium less strict
else if (L.number == LAYER_BPLACE) silk_bottom = 1; // Could also take BNAMES/BVALUES. Leave the criterium less strict
}
SolderSides = smask_top ? (smask_bottom ? "Both Sides" : "Top Side") :
(smask_bottom ? "Bottom Side" : "None");
SilkScreenSides = (silk_top) ? (silk_bottom ? "Both Sides" : "Top Side") :
(silk_bottom ? "Bottom Side" : "None");
if (nr_used_layers == 0) {
Warnings += LItem(tr("None of the signal layers are used."));
Useless = 1;
}
// Board outlines:
UpdateBBox(LAYER_DIMENSION, 0);
if (!ValidBBox()) {
Warnings += "" + tr("Board width and length could not be determined from dimension layer. ");
UpdateBBox(LAYER_TOP, 1);
UpdateBBox(LAYER_BOTTOM, 1);
if (!ValidBBox())
Warnings += tr("Estimate based on outer layers failed. ") +
tr("Please use the dimension layer to design the board outline.") + "";
else
Warnings += tr("Estimation based on outer layers. ") + "";
}
CalcMinTraceWidth();
if (MinTraceWidth == INT_MAX)
Warnings += LItem(tr("No copper found in the signal layers. ") +
tr("Minimum trace width could not be determined."));
if (MinTraceWidth == 0)
Warnings += LItem(tr("Wire(s) and/or polygon(s) with zero width found on signal layer(s). ") +
tr("Minimum trace width could not be determined."));
GetDruParams();
CheckHoles();
int air_wires = 0;
{ // Check if there are still airwires:
board(B) {
B.wires(W) if (W.layer == LAYER_UNROUTED) air_wires = 1;
B.signals(S) {
S.polygons(P) if (P.layer == LAYER_UNROUTED) air_wires = 1;
S.wires(W) if (W.layer == LAYER_UNROUTED) air_wires = 1;
}
}
}
if (air_wires)
Warnings += LItem(tr("There are still airwires left."));
// Analysis of elements
// Alternative idea for performance optimization:
// Run SMD analysis over all library packages to avoid multiple runs
// Keep results in Arrays
string keyBGA = "BGA", keyQFN = "QFN";
real sqLimitFinePitch = 0.5 * 0.5; // Square of the threshold for Fine Pitch (in mm)
board(B) B.elements(E) {
int isThruHole, isSMD;
E.package.contacts(C)
if (C.smd) {
isSMD = true;
if (C.smd.layer == LAYER_TOP) ++NrSmdPadsTop;
else ++NrSmdPadsBottom;
}
else
isThruHole = true; // Assuming there are no mixed versions
if (isThruHole) ++NrThruHoles;
else if (isSMD) { // There might also be the case of NO contacts !
// Collect contacts in untransformed state to make geometrical checks !
real conXs[], conYs[];
int nrCons;
B.libraries(L)
if (L.name == E.package.library) L.packages(P)
if (P.name == E.package.name) P.contacts(C) {
conXs[nrCons] = u2mm(C.x);
conYs[nrCons++] = u2mm(C.y);
}
if (strstr(E.name, keyBGA) >= 0 ||
strstr(E.package.name, keyBGA) >= 0 ||
strstr(E.package.description, keyBGA) >= 0) {
// Plausibility check
++NrBGAs;
}
else if (strstr(E.name, keyQFN) >= 0 ||
strstr(E.package.name, keyQFN) >= 0 ||
strstr(E.package.description, keyQFN) >= 0) {
// Plausibility check
++NrQFNs;
}
else {
// if there's a contact pair with distance <= fine pitch limit it's a FINE Pitch, else it's an other SMD
for (int isFP = false, i = 0; i < nrCons && !isFP; ++i)
for (int j = i + 1; j < nrCons && !isFP; ++j)
if ((conXs[j] - conXs[i]) * (conXs[j] - conXs[i]) +
(conYs[j] - conYs[i]) * (conYs[j] - conYs[i]) <= sqLimitFinePitch)
isFP = true;
if (isFP)
++NrFinePitchs;
else
++NrOtherSMDs;
}
}
}
HasSMDBothSides = NrSmdPadsTop * NrSmdPadsBottom > 0;
// Packages (number of unique elements)
// Only those with contacts !
board(B) B.libraries(L) L.packages(P) P.contacts(C) {
++NrPackages;
break;
}
}
string MakeParamSendString() {
// Fab parameters:
// - Parameters that always can be determined:
string send = "?source=SBPCAD" +
"&boardName=" + BoardName +
"&numLayers=" + I2Str(NrLayers) +
"&materialThicknessMm=" + U2MmStr(MaterialThickness) +
"&materialThicknessIn=" + U2InStr(MaterialThickness) +
"&copperThicknessOutsideMm=" + U2MmStr(CopperThicknessOutside) +
"&copperThicknessOutsideOz=" + U2OzStr(CopperThicknessOutside) +
"&solderSides=" + SolderSides +
"&silkScreenSides=" + SilkScreenSides +
"&blindBuriedHoles=" + I2Str(NrBlindBuriedTypes) +
"&numPadsTop=" + I2Str(NrSmdPadsTop) +
"&numPadsBottom=" + I2Str(NrSmdPadsBottom);
// - Parameters that might miss:
if (ValidBBox())
send += "&boardWidthMm=" + U2MmStr(XMax - XMin) +
"&boardWidthIn=" + U2InStr(XMax - XMin) +
"&boardLengthMm=" + U2MmStr(YMax - YMin) +
"&boardLengthIn=" + U2InStr(YMax - YMin);
if (Within(CopperThicknessInside, 0, INT_MAX))
send += "&copperThicknessInsideMm=" + U2MmStr(CopperThicknessInside) +
"&copperThicknessInsideOz=" + U2OzStr(CopperThicknessInside);
if (Within(MinTraceWidth, 0, INT_MAX))
send += "&minTraceWidthMm=" + U2MmStr(MinTraceWidth) +
"&minTraceWidthIn=" + U2InStr(MinTraceWidth);
if (MinPitch < INT_MAX)
send += "&minPitchMm=" + U2MmStr(MinPitch) +
"&minPitchIn=" + U2InStr(MinPitch);
if (MinHoleSize < INT_MAX)
send += "&minHoleSizeMm=" + U2MmStr(MinHoleSize) +
"&minHoleSizeIn=" + U2InStr(MinHoleSize);
// Assy parameters:
send += "&numPackages=" + I2Str(NrPackages) +
"&numBGAs=" + I2Str(NrBGAs) +
"&numQFNs=" + I2Str(NrQFNs) +
"&numFinePitch=" + I2Str(NrFinePitchs) +
"&numOtherSMDs=" + I2Str(NrOtherSMDs) +
"&numThruHoles=" + I2Str(NrThruHoles) +
"&hasSMDBothSides=" + B2Str(HasSMDBothSides);
if (Dbg) output(DbgFile, "wba") printf("%s\n%s\n", GetTargetURL(), send);
return send;
}
int Dialog() {
string Info[] = {
"Based on your board layout and the design rules, key parameters for manufacturing "
"your board like board size, minimum hole size etc. are determined. Your design "
"should be complete and have passed a DRC successfully.
"
"By following the link below you get to the PCB quote site on Element14 where these "
"parameters are transferred. This way with a few steps you get a quote for manufacturing "
"your board."
,
"Basierend auf Ihr Board-Layout und den Designregeln werden wichtige Parameter zur Fertigung "
"des Boards ermittelt wie zum Beispiel Boardgrösse, kleinster Bohrdurchmesser usw. "
"Ihr Design sollte möglichst vollständig und ein erfolgreicher DRC durchgeführt worden sein.
"
"Durch Klicken des Links unten gelangen Sie zur PCB-Angebotsseite auf Element14, wo diese "
"Parameter übernommen werden. Mit wenigen Schritten erhalten Sie so ein Angebot zur Fertigung "
"Ihres Boards."
};
string url = "";
return dlgDialog(tr("PCB Quote Service (Version " + I2Str(Version) + ")")) {
dlgHBoxLayout dlgSpacing(470);
dlgGroup(tr("Information:"))
dlgLabel(Info[LangIdx], 1);
if (Warnings)
dlgHBoxLayout
dlgGroup(tr("Warnings:"))
dlgLabel(Warnings);
if (!Useless) {
dlgGroup(tr("Fabrication Parameters:"))
dlgGridLayout {
int Row;
dlgCell(Row, 0) dlgLabel(tr("Number of layers:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrLayers)));
dlgCell(Row, 0) dlgLabel(tr("Board name:")); dlgCell(Row++, 1) dlgLabel(Bold(BoardName));
dlgCell(Row, 0) dlgLabel(tr("Board width (dimension X):")); dlgCell(Row++, 1) dlgLabel(Bold(ValidBBox() ? U2PrefUStrExt(XMax - XMin) : Unknown));
dlgCell(Row, 0) dlgLabel(tr("Board length (dimension Y):")); dlgCell(Row++, 1) dlgLabel(Bold(ValidBBox() ? U2PrefUStrExt(YMax - YMin) : Unknown));
dlgCell(Row, 0) dlgLabel(tr("Board thickness:")); dlgCell(Row++, 1) dlgLabel(Bold(U2PrefUStrExt(MaterialThickness)));
dlgCell(Row, 0) dlgLabel(tr("Copper thickness outer layers:")); dlgCell(Row++, 1) dlgLabel(Bold(U2PrefUStrExt(CopperThicknessOutside)));
dlgCell(Row, 0) dlgLabel(tr("Copper thickness inner layers:")); dlgCell(Row++, 1) dlgLabel(Bold(Within(CopperThicknessInside, 0, INT_MAX) ? U2PrefUStrExt(CopperThicknessInside) : Unknown));
dlgCell(Row, 0) dlgLabel(tr("Solder sides:")); dlgCell(Row++, 1) dlgLabel(Bold(tr(SolderSides)));
dlgCell(Row, 0) dlgLabel(tr("Silkscreen sides:")); dlgCell(Row++, 1) dlgLabel(Bold(tr(SilkScreenSides)));
dlgCell(Row, 0) dlgLabel(tr("Number of SMD pads on top:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrSmdPadsTop)));
dlgCell(Row, 0) dlgLabel(tr("Number of SMD pads on bottom:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrSmdPadsBottom)));
dlgCell(Row, 0) dlgLabel(tr("Number of blind or buried hole types:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrBlindBuriedTypes)));
dlgCell(Row, 0) dlgLabel(tr("Minimum trace width (track width):")); dlgCell(Row++, 1) dlgLabel(Bold(Within(MinTraceWidth, 0, INT_MAX) ? U2PrefUStrExt(MinTraceWidth) : Unknown));
dlgCell(Row, 0) dlgLabel(tr("Minimum SMD pitch:")); dlgCell(Row++, 1) dlgLabel(Bold(MinPitch < INT_MAX ? U2PrefUStrExt(MinPitch) : Unknown));
dlgCell(Row, 0) dlgLabel(tr("Minimum hole size:")); dlgCell(Row++, 1) dlgLabel(Bold(MinHoleSize < INT_MAX ? U2PrefUStrExt(MinHoleSize) : Unknown));
}
dlgGroup(tr("Assembly Parameters:"))
dlgGridLayout {
int Row;
dlgCell(Row, 0) dlgLabel(tr("Number of different packages:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrPackages)));
dlgCell(Row, 0) dlgLabel(tr("Number of") + " BGAs:"); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrBGAs)));
dlgCell(Row, 0) dlgLabel(tr("Number of") + " QFNs:"); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrQFNs)));
dlgCell(Row, 0) dlgLabel(tr("Number of fine pitch packages:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrFinePitchs)));
dlgCell(Row, 0) dlgLabel(tr("Number of other SMDs:")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrOtherSMDs)));
dlgCell(Row, 0) dlgLabel(tr("Number of thru hole packages: ")); dlgCell(Row++, 1) dlgLabel(Bold(I2Str(NrThruHoles)));
dlgCell(Row, 0) dlgLabel(tr("SMDs on both sides:")); dlgCell(Row++, 1) dlgLabel(Bold(HasSMDBothSides ? tr("Yes") : tr("No")));
}
}
dlgHBoxLayout {
dlgStretch(1);
if (!Useless)
dlgLabel("" + url + tr("Get PCB Quote") + "", 1);
dlgStretch(1);
dlgPushButton(tr("Close")) dlgReject();
}
};
}
//-----------------------------------------------------------------------------
// main section
//-----------------------------------------------------------------------------
// Parse arguments
for (int i = 1; i <= argc; ++i) {
if (argv[i] == "-noexp") Export = 0;
}
if (board) {
string dir = filedir(argv[0]);
board(B) DruFile = dir + "dl-" + filename(filesetext(B.name, ".dru"));
if (Export)
exit("DRC SAVE '" + DruFile + "';\nRUN '" + argv[0] + "' -noexp;");
if (Dbg) board(B) {
DbgFile = filedir(B.name) + "/dbg-pcb-service.txt";
output(DbgFile, "wba") printf("\nBoard: %s\n", filename(B.name));
}
status(tr("Calculating quote parameters..."));
GetPCBParams();
if (Warnings) {
string tmp = tr("Your board layout is incomplete:") + "";
if (Useless) tmp += tr("A quote for this board doesn't make sense yet !");
else tmp += tr("If you want to get a quote anyway, please follow the link.");
Warnings = tmp;
}
if (Dbg && Warnings) output(DbgFile, "wba") printf("Warnings: \n%s\n", Warnings);
board(B) {
int grU = B.grid.unit;
PrefUnit = (grU == GRID_UNIT_MM || grU == GRID_UNIT_MIC) ? GRID_UNIT_MM : GRID_UNIT_INCH;
}
while (Dialog() == 1);
exit(EXIT_SUCCESS);
}
else
dlgMessageBox(tr("Please start from board editor !"));