pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00042 #ifdef HAVE_CONFIG_H 00043 #include "config.h" 00044 #endif 00045 00046 #include <stdio.h> 00047 #include <stdarg.h> 00048 #include <stdlib.h> 00049 #include <string.h> 00050 #include <time.h> 00051 00052 #include "global.h" 00053 #include "data.h" 00054 #include "error.h" 00055 #include "misc.h" 00056 #include "pcb-printf.h" 00057 00058 #include "hid.h" 00059 #include "hid/common/hidnogui.h" 00060 #include "../hidint.h" 00061 00062 #ifdef HAVE_LIBDMALLOC 00063 #include <dmalloc.h> 00064 #endif 00065 00066 static HID_Attribute bom_options[] = { 00067 /* %start-doc options "80 BOM Creation" 00068 @ftable @code 00069 @item --bomfile <string> 00070 Name of the BOM output file. 00071 Parameter @code{<string>} can include a path. 00072 @end ftable 00073 %end-doc 00074 */ 00075 {"bomfile", "Name of the BOM output file", 00076 HID_String, 0, 0, {0, 0, 0}, 0, 0}, 00077 #define HA_bomfile 0 00078 /* %start-doc options "80 BOM Creation" 00079 @ftable @code 00080 @item --xyfile <string> 00081 Name of the XY output file. 00082 Parameter @code{<string>} can include a path. 00083 @end ftable 00084 %end-doc 00085 */ 00086 {"xyfile", "Name of the XY output file", 00087 HID_String, 0, 0, {0, 0, 0}, 0, 0}, 00088 #define HA_xyfile 1 00089 00090 /* %start-doc options "80 BOM Creation" 00091 @ftable @code 00092 @item --xy-unit <unit> 00093 Unit of XY dimensions. Defaults to mil. 00094 Parameter @code{<unit>} can be @samp{km}, @samp{m}, @samp{cm}, @samp{mm}, 00095 @samp{um}, @samp{nm}, @samp{px}, @samp{in}, @samp{mil}, @samp{dmil}, 00096 @samp{cmil}, or @samp{inch}. 00097 @end ftable 00098 %end-doc 00099 */ 00100 {"xy-unit", "XY units", 00101 HID_Unit, 0, 0, {-1, 0, 0}, NULL, 0}, 00102 #define HA_unit 2 00103 {"xy-in-mm", ATTR_UNDOCUMENTED, 00104 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, 00105 #define HA_xymm 3 00106 }; 00107 00108 #define NUM_OPTIONS (sizeof(bom_options)/sizeof(bom_options[0])) 00109 00110 static HID_Attr_Val bom_values[NUM_OPTIONS]; 00111 00112 static const char *bom_filename; 00113 static const char *xy_filename; 00114 static const Unit *xy_unit; 00115 00116 typedef struct _StringList 00117 { 00118 char *str; 00119 struct _StringList *next; 00120 } StringList; 00121 00122 typedef struct _BomList 00123 { 00124 char *descr; 00125 char *value; 00126 int num; 00127 StringList *refdes; 00128 struct _BomList *next; 00129 } BomList; 00130 00131 static HID_Attribute * 00132 bom_get_export_options (int *n) 00133 { 00134 static char *last_bom_filename = 0; 00135 static char *last_xy_filename = 0; 00136 static int last_unit_value = -1; 00137 00138 if (bom_options[HA_unit].default_val.int_value == last_unit_value) 00139 { 00140 if (Settings.grid_unit) 00141 bom_options[HA_unit].default_val.int_value = Settings.grid_unit->index; 00142 else 00143 bom_options[HA_unit].default_val.int_value = get_unit_struct ("mil")->index; 00144 last_unit_value = bom_options[HA_unit].default_val.int_value; 00145 } 00146 if (PCB) { 00147 derive_default_filename(PCB->Filename, &bom_options[HA_bomfile], ".bom", &last_bom_filename); 00148 derive_default_filename(PCB->Filename, &bom_options[HA_xyfile ], ".xy" , &last_xy_filename ); 00149 } 00150 00151 if (n) 00152 *n = NUM_OPTIONS; 00153 return bom_options; 00154 } 00155 00156 static char * 00157 CleanBOMString (char *in) 00158 { 00159 char *out; 00160 int i; 00161 00162 if ((out = (char *)malloc ((strlen (in) + 1) * sizeof (char))) == NULL) 00163 { 00164 fprintf (stderr, "Error: CleanBOMString() malloc() failed\n"); 00165 exit (1); 00166 } 00167 00168 /* 00169 * copy over in to out with some character conversions. 00170 * Go all the way to then end to get the terminating \0 00171 */ 00172 for (i = 0; i <= strlen (in); i++) 00173 { 00174 switch (in[i]) 00175 { 00176 case '"': 00177 out[i] = '\''; 00178 break; 00179 default: 00180 out[i] = in[i]; 00181 } 00182 } 00183 00184 return out; 00185 } 00186 00187 00188 static double 00189 xyToAngle (double x, double y, bool morethan2pins) 00190 { 00191 double d = atan2 (-y, x) * 180.0 / M_PI; 00192 00193 /* IPC 7351 defines different rules for 2 pin elements */ 00194 if (morethan2pins) 00195 { 00196 /* Multi pin case: 00197 * Output 0 degrees if pin1 in is top left or top, i.e. between angles of 00198 * 80 to 170 degrees. 00199 * Pin #1 can be at dead top (e.g. certain PLCCs) or anywhere in the top 00200 * left. 00201 */ 00202 if (d < -100) 00203 return 90; /* -180 to -100 */ 00204 else if (d < -10) 00205 return 180; /* -100 to -10 */ 00206 else if (d < 80) 00207 return 270; /* -10 to 80 */ 00208 else if (d < 170) 00209 return 0; /* 80 to 170 */ 00210 else 00211 return 90; /* 170 to 180 */ 00212 } 00213 else 00214 { 00215 /* 2 pin element: 00216 * Output 0 degrees if pin #1 is in top left or left, i.e. in sector 00217 * between angles of 95 and 185 degrees. 00218 */ 00219 if (d < -175) 00220 return 0; /* -180 to -175 */ 00221 else if (d < -85) 00222 return 90; /* -175 to -85 */ 00223 else if (d < 5) 00224 return 180; /* -85 to 5 */ 00225 else if (d < 95) 00226 return 270; /* 5 to 95 */ 00227 else 00228 return 0; /* 95 to 180 */ 00229 } 00230 } 00231 00232 static StringList * 00233 string_insert (char *str, StringList * list) 00234 { 00235 StringList *newlist, *cur; 00236 00237 if ((newlist = (StringList *) malloc (sizeof (StringList))) == NULL) 00238 { 00239 fprintf (stderr, "malloc() failed in string_insert()\n"); 00240 exit (1); 00241 } 00242 00243 newlist->next = NULL; 00244 newlist->str = strdup (str); 00245 00246 if (list == NULL) 00247 return (newlist); 00248 00249 cur = list; 00250 while (cur->next != NULL) 00251 cur = cur->next; 00252 00253 cur->next = newlist; 00254 00255 return (list); 00256 } 00257 00258 static BomList * 00259 bom_insert (char *refdes, char *descr, char *value, BomList * bom) 00260 { 00261 BomList *newlist, *cur, *prev = NULL; 00262 00263 if (bom == NULL) 00264 { 00265 /* this is the first element so automatically create an entry */ 00266 if ((newlist = (BomList *) malloc (sizeof (BomList))) == NULL) 00267 { 00268 fprintf (stderr, "malloc() failed in bom_insert()\n"); 00269 exit (1); 00270 } 00271 00272 newlist->next = NULL; 00273 newlist->descr = strdup (descr); 00274 newlist->value = strdup (value); 00275 newlist->num = 1; 00276 newlist->refdes = string_insert (refdes, NULL); 00277 return (newlist); 00278 } 00279 00280 /* search and see if we already have used one of these 00281 components */ 00282 cur = bom; 00283 while (cur != NULL) 00284 { 00285 if ((NSTRCMP (descr, cur->descr) == 0) && 00286 (NSTRCMP (value, cur->value) == 0)) 00287 { 00288 cur->num++; 00289 cur->refdes = string_insert (refdes, cur->refdes); 00290 break; 00291 } 00292 prev = cur; 00293 cur = cur->next; 00294 } 00295 00296 if (cur == NULL) 00297 { 00298 if ((newlist = (BomList *) malloc (sizeof (BomList))) == NULL) 00299 { 00300 fprintf (stderr, "malloc() failed in bom_insert()\n"); 00301 exit (1); 00302 } 00303 00304 prev->next = newlist; 00305 00306 newlist->next = NULL; 00307 newlist->descr = strdup (descr); 00308 newlist->value = strdup (value); 00309 newlist->num = 1; 00310 newlist->refdes = string_insert (refdes, NULL); 00311 } 00312 00313 return (bom); 00314 00315 } 00316 00322 static void 00323 print_and_free (FILE *fp, BomList *bom) 00324 { 00325 BomList *lastb; 00326 StringList *lasts; 00327 char *descr, *value; 00328 00329 while (bom != NULL) 00330 { 00331 if (fp) 00332 { 00333 descr = CleanBOMString (bom->descr); 00334 value = CleanBOMString (bom->value); 00335 fprintf (fp, "%d,\"%s\",\"%s\",", bom->num, descr, value); 00336 free (descr); 00337 free (value); 00338 } 00339 00340 while (bom->refdes != NULL) 00341 { 00342 if (fp) 00343 { 00344 fprintf (fp, "%s ", bom->refdes->str); 00345 } 00346 free (bom->refdes->str); 00347 lasts = bom->refdes; 00348 bom->refdes = bom->refdes->next; 00349 free (lasts); 00350 } 00351 if (fp) 00352 { 00353 fprintf (fp, "\n"); 00354 } 00355 lastb = bom; 00356 bom = bom->next; 00357 free (lastb); 00358 } 00359 } 00360 00364 #define MAXREFPINS 32 00365 00372 static char *reference_pin_names[] = {"1", "2", "A1", "A2", "B1", "B2", 0}; 00373 00374 static int 00375 PrintBOM (void) 00376 { 00377 char utcTime[64]; 00378 Coord x, y; 00379 double theta = 0.0; 00380 double sumx, sumy; 00381 int pinfound[MAXREFPINS]; 00382 double pinx[MAXREFPINS]; 00383 double piny[MAXREFPINS]; 00384 double pinangle[MAXREFPINS]; 00385 double padcentrex, padcentrey; 00386 double centroidx, centroidy; 00387 double pin1x, pin1y; 00388 int pin_cnt; 00389 int found_any_not_at_centroid; 00390 int found_any; 00391 time_t currenttime; 00392 FILE *fp; 00393 BomList *bom = NULL; 00394 char *name, *descr, *value,*fixed_rotation; 00395 int rpindex; 00396 00397 fp = fopen (xy_filename, "w"); 00398 if (!fp) 00399 { 00400 gui->log ("Cannot open file %s for writing\n", xy_filename); 00401 return 1; 00402 } 00403 00404 /* Create a portable timestamp. */ 00405 currenttime = time (NULL); 00406 { 00407 /* avoid gcc complaints */ 00408 const char *fmt = "%c UTC"; 00409 strftime (utcTime, sizeof (utcTime), fmt, gmtime (¤ttime)); 00410 } 00411 fprintf (fp, "# PcbXY Version 1.0\n"); 00412 fprintf (fp, "# Date: %s\n", utcTime); 00413 fprintf (fp, "# Author: %s\n", pcb_author ()); 00414 fprintf (fp, "# Title: %s - PCB X-Y\n", UNKNOWN (PCB->Name)); 00415 fprintf (fp, "# RefDes, Description, Value, X, Y, rotation, top/bottom\n"); 00416 /* don't use localized xy_unit->in_suffix here since */ 00417 /* the line itself is not localized and not for GUI */ 00418 fprintf (fp, "# X,Y in %s. rotation in degrees.\n", xy_unit->suffix); 00419 fprintf (fp, "# --------------------------------------------\n"); 00420 00421 /* 00422 * For each element we calculate the centroid of the footprint. 00423 * In addition, we need to extract some notion of rotation. 00424 * While here generate the BOM list 00425 */ 00426 00427 ELEMENT_LOOP (PCB->Data); 00428 { 00429 00430 /* Initialize our pin count and our totals for finding the centroid. */ 00431 pin_cnt = 0; 00432 sumx = 0.0; 00433 sumy = 0.0; 00434 for (rpindex = 0; rpindex < MAXREFPINS; rpindex++) 00435 pinfound[rpindex] = 0; 00436 00437 /* Insert this component into the bill of materials list. */ 00438 bom = bom_insert ((char *)UNKNOWN (NAMEONPCB_NAME (element)), 00439 (char *)UNKNOWN (DESCRIPTION_NAME (element)), 00440 (char *)UNKNOWN (VALUE_NAME (element)), bom); 00441 00442 00443 /* 00444 * Iterate over the pins and pads keeping a running count of how 00445 * many pins/pads total and the sum of x and y coordinates 00446 * 00447 * While we're at it, store the location of pin/pad #1 and #2 if 00448 * we can find them. 00449 */ 00450 00451 PIN_LOOP (element); 00452 { 00453 sumx += (double) pin->X; 00454 sumy += (double) pin->Y; 00455 pin_cnt++; 00456 00457 for (rpindex = 0; reference_pin_names[rpindex]; rpindex++) 00458 { 00459 if (NSTRCMP (pin->Number, reference_pin_names[rpindex]) == 0) 00460 { 00461 pinx[rpindex] = (double) pin->X; 00462 piny[rpindex] = (double) pin->Y; 00463 pinangle[rpindex] = 0.0; /* pins have no notion of angle */ 00464 pinfound[rpindex] = 1; 00465 } 00466 } 00467 } 00468 END_LOOP; 00469 00470 PAD_LOOP (element); 00471 { 00472 sumx += (pad->Point1.X + pad->Point2.X) / 2.0; 00473 sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0; 00474 pin_cnt++; 00475 00476 for (rpindex = 0; reference_pin_names[rpindex]; rpindex++) 00477 { 00478 if (NSTRCMP (pad->Number, reference_pin_names[rpindex]) == 0) 00479 { 00480 padcentrex = (double) (pad->Point1.X + pad->Point2.X) / 2.0; 00481 padcentrey = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0; 00482 pinx[rpindex] = padcentrex; 00483 piny[rpindex] = padcentrey; 00484 /* 00485 * NOTE: We swap the Y points because in PCB, the Y-axis 00486 * is inverted. Increasing Y moves down. We want to deal 00487 * in the usual increasing Y moves up coordinates though. 00488 */ 00489 pinangle[rpindex] = (180.0 / M_PI) * atan2 (pad->Point1.Y - pad->Point2.Y, 00490 pad->Point2.X - pad->Point1.X); 00491 pinfound[rpindex]=1; 00492 } 00493 } 00494 } 00495 END_LOOP; 00496 00497 if (pin_cnt > 0) 00498 { 00499 centroidx = sumx / (double) pin_cnt; 00500 centroidy = sumy / (double) pin_cnt; 00501 00502 if (NSTRCMP( AttributeGetFromList (&element->Attributes,"xy-centre"), "origin") == 0 ) 00503 { 00504 x = element->MarkX; 00505 y = element->MarkY; 00506 } 00507 else 00508 { 00509 x = centroidx; 00510 y = centroidy; 00511 } 00512 00513 fixed_rotation = AttributeGetFromList (&element->Attributes, "xy-fixed-rotation"); 00514 if (fixed_rotation) 00515 { 00516 /* The user specified a fixed rotation */ 00517 theta = atof (fixed_rotation); 00518 found_any_not_at_centroid = 1; 00519 found_any = 1; 00520 } 00521 else 00522 { 00523 /* Find first reference pin not at the centroid */ 00524 found_any_not_at_centroid = 0; 00525 found_any = 0; 00526 theta = 0.0; 00527 for (rpindex = 0; 00528 reference_pin_names[rpindex] && !found_any_not_at_centroid; 00529 rpindex++) 00530 { 00531 if (pinfound[rpindex]) 00532 { 00533 found_any = 1; 00534 00535 /* Recenter pin "#1" onto the axis which cross at the part 00536 centroid */ 00537 pin1x = pinx[rpindex] - x; 00538 pin1y = piny[rpindex] - y; 00539 00540 /* flip x, to reverse rotation for elements on back */ 00541 if (FRONT (element) != 1) 00542 pin1x = -pin1x; 00543 00544 /* if only 1 pin, use pin 1's angle */ 00545 if (pin_cnt == 1) 00546 { 00547 theta = pinangle[rpindex]; 00548 found_any_not_at_centroid = 1; 00549 } 00550 else if ((pin1x != 0.0) || (pin1y != 0.0)) 00551 { 00552 theta = xyToAngle (pin1x, pin1y, pin_cnt > 2); 00553 found_any_not_at_centroid = 1; 00554 } 00555 } 00556 } 00557 00558 if (!found_any) 00559 { 00560 Message 00561 ("PrintBOM(): unable to figure out angle because I could\n" 00562 " not find a suitable reference pin of element %s\n" 00563 " Setting to %g degrees\n", 00564 UNKNOWN (NAMEONPCB_NAME (element)), theta); 00565 } 00566 else if (!found_any_not_at_centroid) 00567 { 00568 Message 00569 ("PrintBOM(): unable to figure out angle of element\n" 00570 " %s because the reference pin(s) are at the centroid of the part.\n" 00571 " Setting to %g degrees\n", 00572 UNKNOWN (NAMEONPCB_NAME (element)), theta); 00573 } 00574 } 00575 name = CleanBOMString ((char *)UNKNOWN (NAMEONPCB_NAME (element))); 00576 descr = CleanBOMString ((char *)UNKNOWN (DESCRIPTION_NAME (element))); 00577 value = CleanBOMString ((char *)UNKNOWN (VALUE_NAME (element))); 00578 00579 y = PCB->MaxHeight - y; 00580 pcb_fprintf (fp, "%m+%s,\"%s\",\"%s\",%.2`mS,%.2`mS,%g,%s\n", 00581 xy_unit->allow, name, descr, value, x, y, 00582 theta, FRONT (element) == 1 ? "top" : "bottom"); 00583 free (name); 00584 free (descr); 00585 free (value); 00586 } 00587 } 00588 END_LOOP; 00589 00590 fclose (fp); 00591 00592 /* Now print out a Bill of Materials file */ 00593 00594 fp = fopen (bom_filename, "w"); 00595 if (!fp) 00596 { 00597 gui->log ("Cannot open file %s for writing\n", bom_filename); 00598 print_and_free (NULL, bom); 00599 return 1; 00600 } 00601 00602 fprintf (fp, "# PcbBOM Version 1.0\n"); 00603 fprintf (fp, "# Date: %s\n", utcTime); 00604 fprintf (fp, "# Author: %s\n", pcb_author ()); 00605 fprintf (fp, "# Title: %s - PCB BOM\n", UNKNOWN (PCB->Name)); 00606 fprintf (fp, "# Quantity, Description, Value, RefDes\n"); 00607 fprintf (fp, "# --------------------------------------------\n"); 00608 00609 print_and_free (fp, bom); 00610 00611 fclose (fp); 00612 00613 return (0); 00614 } 00615 00616 static void 00617 bom_do_export (HID_Attr_Val * options) 00618 { 00619 int i; 00620 00621 if (!options) 00622 { 00623 bom_get_export_options (0); 00624 for (i = 0; i < NUM_OPTIONS; i++) 00625 bom_values[i] = bom_options[i].default_val; 00626 options = bom_values; 00627 } 00628 00629 bom_filename = options[HA_bomfile].str_value; 00630 if (!bom_filename) 00631 bom_filename = "pcb-out.bom"; 00632 00633 xy_filename = options[HA_xyfile].str_value; 00634 if (!xy_filename) 00635 xy_filename = "pcb-out.xy"; 00636 00637 if (options[HA_xymm].int_value) 00638 xy_unit = get_unit_struct ("mm"); 00639 else 00640 xy_unit = &get_unit_list ()[options[HA_unit].int_value]; 00641 PrintBOM (); 00642 } 00643 00644 static void 00645 bom_parse_arguments (int *argc, char ***argv) 00646 { 00647 hid_register_attributes (bom_options, 00648 sizeof (bom_options) / sizeof (bom_options[0])); 00649 hid_parse_command_line (argc, argv); 00650 } 00651 00652 HID bom_hid; 00653 00654 void 00655 hid_bom_init () 00656 { 00657 memset (&bom_hid, 0, sizeof (HID)); 00658 00659 common_nogui_init (&bom_hid); 00660 00661 bom_hid.struct_size = sizeof (HID); 00662 bom_hid.name = "bom"; 00663 bom_hid.description = "Exports a Bill of Materials"; 00664 bom_hid.exporter = 1; 00665 00666 bom_hid.get_export_options = bom_get_export_options; 00667 bom_hid.do_export = bom_do_export; 00668 bom_hid.parse_arguments = bom_parse_arguments; 00669 00670 hid_register_hid (&bom_hid); 00671 }