6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996,1997,1998,1999 Thomas Nau
9 * This module, report.c, was written and is Copyright (C) 1997 harry eaton
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * Contact addresses for paper mail and Email:
26 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
27 * Thomas.Nau@rz.uni-ulm.de
39 #include "crosshair.h"
52 #ifdef HAVE_LIBDMALLOC
59 #define UNIT1(value) (Settings.grid_units_mm ? ((value) / 100000.0 * 25.4) : ((value) / 100.0))
60 #define UNIT(value) UNIT1(value) , (Settings.grid_units_mm ? "mm" : "mils")
63 ReportDrills (int argc
, char **argv
, int x
, int y
)
65 DrillInfoTypePtr AllDrills
;
67 char *stringlist
, *thestring
;
70 AllDrills
= GetDrillInfo (PCB
->Data
);
71 RoundDrillInfo (AllDrills
, 100);
73 for (n
= 0; n
< AllDrills
->DrillN
; n
++)
75 total_drills
+= AllDrills
->Drill
[n
].PinCount
;
76 total_drills
+= AllDrills
->Drill
[n
].ViaCount
;
77 total_drills
+= AllDrills
->Drill
[n
].UnplatedCount
;
80 stringlist
= malloc (512L + AllDrills
->DrillN
* 64L);
82 /* Use tabs for formatting since can't count on a fixed font anymore.
83 | And even that probably isn't going to work in all cases.
86 "There are %d different drill sizes used in this layout, %d holes total\n\n"
87 "Drill Diam. (mils)\t# of Pins\t# of Vias\t# of Elements\t# Unplated\n",
88 AllDrills
->DrillN
, total_drills
);
89 thestring
= stringlist
;
90 while (*thestring
!= '\0')
92 for (n
= 0; n
< AllDrills
->DrillN
; n
++)
95 "\t%d\t\t\t%d\t\t%d\t\t%d\t\t%d\n",
96 (AllDrills
->Drill
[n
].DrillSize
+50) / 100,
97 AllDrills
->Drill
[n
].PinCount
, AllDrills
->Drill
[n
].ViaCount
,
98 AllDrills
->Drill
[n
].ElementN
,
99 AllDrills
->Drill
[n
].UnplatedCount
);
100 while (*thestring
!= '\0')
103 FreeDrillInfo (AllDrills
);
104 /* create dialog box */
105 gui
->report_dialog ("Drill Report", stringlist
);
107 SaveFree (stringlist
);
112 static const char reportdialog_syntax
[] = "ReportDialog()";
114 static const char reportdialog_help
[] =
115 "Report on the object under the crosshair";
117 /* %start-doc actions ReportDialog
119 This is a shortcut for @code{Report(Object)}.
124 ReportDialog (int argc
, char **argv
, int x
, int y
)
126 void *ptr1
, *ptr2
, *ptr3
;
130 type
= SearchScreen (x
, y
, REPORT_TYPES
, &ptr1
, &ptr2
, &ptr3
);
133 SearchScreen (x
, y
, REPORT_TYPES
| LOCKED_TYPE
, &ptr1
, &ptr2
, &ptr3
);
141 if (gui
->shift_is_pressed ())
143 __r_dump_tree (PCB
->Data
->via_tree
->root
, 0);
147 via
= (PinTypePtr
) ptr2
;
148 if (TEST_FLAG (HOLEFLAG
, via
))
149 sprintf (&report
[0], "VIA ID# %ld Flags:%s\n"
151 "It is a pure hole of diameter %0.2f %s\n"
153 "%s", via
->ID
, flags_to_string (via
->Flags
, VIA_TYPE
),
154 via
->X
, via
->Y
, UNIT (via
->DrillingHole
),
155 EMPTY (via
->Name
), TEST_FLAG (LOCKFLAG
,
156 via
) ? "It is LOCKED\n" :
159 sprintf (&report
[0], "VIA ID# %ld Flags:%s\n"
161 "Copper width = %0.2f %s Drill width = %0.2f %s\n"
162 "Clearance width in polygons = %0.2f %s\n"
163 "Annulus = %0.2f %s\n"
164 "Solder mask hole = %0.2f %s (gap = %0.2f %s)\n"
166 "%s", via
->ID
, flags_to_string (via
->Flags
, VIA_TYPE
),
167 via
->X
, via
->Y
, UNIT (via
->Thickness
),
168 UNIT (via
->DrillingHole
), UNIT (via
->Clearance
/ 2.),
169 UNIT ((via
->Thickness
- via
->DrillingHole
)/2),
170 UNIT (via
->Mask
), UNIT ((via
->Mask
- via
->Thickness
)/2),
171 EMPTY (via
->Name
), TEST_FLAG (LOCKFLAG
, via
) ?
172 "It is LOCKED\n" : "");
178 ElementTypePtr element
;
180 if (gui
->shift_is_pressed ())
182 __r_dump_tree (PCB
->Data
->pin_tree
->root
, 0);
186 Pin
= (PinTypePtr
) ptr2
;
187 element
= (ElementTypePtr
) ptr1
;
195 if (TEST_FLAG (HOLEFLAG
, Pin
))
196 sprintf (&report
[0], "PIN ID# %ld Flags:%s\n"
198 "It is a mounting hole, Drill width = %0.2f %s\n"
199 "It is owned by element %s\n"
200 "%s", Pin
->ID
, flags_to_string (Pin
->Flags
, PIN_TYPE
),
201 Pin
->X
, Pin
->Y
, UNIT (Pin
->DrillingHole
),
202 EMPTY (element
->Name
[1].TextString
),
203 TEST_FLAG (LOCKFLAG
, Pin
) ? "It is LOCKED\n" : "");
206 "PIN ID# %ld Flags:%s\n" "(X,Y) = (%d, %d)\n"
207 "Copper width = %0.2f %s Drill width = %0.2f %s\n"
208 "Clearance width to Polygon = %0.2f %s\n"
209 "Annulus = %0.2f %s\n"
210 "Solder mask hole = %0.2f %s (gap = %0.2f %s)\n"
212 "It is owned by element %s\n" "As pin number %s\n"
214 Pin
->ID
, flags_to_string (Pin
->Flags
, PIN_TYPE
),
215 Pin
->X
, Pin
->Y
, UNIT (Pin
->Thickness
),
216 UNIT (Pin
->DrillingHole
), UNIT (Pin
->Clearance
/ 2.),
217 UNIT ((Pin
->Thickness
- Pin
->DrillingHole
)/2),
218 UNIT (Pin
->Mask
), UNIT ((Pin
->Mask
- Pin
->Thickness
)/2),
220 EMPTY (element
->Name
[1].TextString
), EMPTY (Pin
->Number
),
221 TEST_FLAG (LOCKFLAG
, Pin
) ? "It is LOCKED\n" : "");
228 if (gui
->shift_is_pressed ())
230 LayerTypePtr layer
= (LayerTypePtr
) ptr1
;
231 __r_dump_tree (layer
->line_tree
->root
, 0);
235 line
= (LineTypePtr
) ptr2
;
236 sprintf (&report
[0], "LINE ID# %ld Flags:%s\n"
237 "FirstPoint(X,Y) = (%d, %d) ID = %ld\n"
238 "SecondPoint(X,Y) = (%d, %d) ID = %ld\n"
239 "Width = %0.2f %s.\nClearance width in polygons = %0.2f %s.\n"
240 "It is on layer %d\n"
243 line
->ID
, flags_to_string (line
->Flags
, LINE_TYPE
),
244 line
->Point1
.X
, line
->Point1
.Y
,
245 line
->Point1
.ID
, line
->Point2
.X
, line
->Point2
.Y
,
246 line
->Point2
.ID
, UNIT (line
->Thickness
),
247 UNIT (line
->Clearance
/ 2.), GetLayerNumber (PCB
->Data
,
248 (LayerTypePtr
) ptr1
),
249 UNKNOWN (line
->Number
), TEST_FLAG (LOCKFLAG
,
250 line
) ? "It is LOCKED\n" :
258 if (gui
->shift_is_pressed ())
260 __r_dump_tree (PCB
->Data
->rat_tree
->root
, 0);
264 line
= (RatTypePtr
) ptr2
;
265 sprintf (&report
[0], "RAT-LINE ID# %ld Flags:%s\n"
266 "FirstPoint(X,Y) = (%d, %d) ID = %ld "
267 "connects to layer group %d\n"
268 "SecondPoint(X,Y) = (%d, %d) ID = %ld "
269 "connects to layer group %d\n",
270 line
->ID
, flags_to_string (line
->Flags
, LINE_TYPE
),
271 line
->Point1
.X
, line
->Point1
.Y
,
272 line
->Point1
.ID
, line
->group1
,
273 line
->Point2
.X
, line
->Point2
.Y
,
274 line
->Point2
.ID
, line
->group2
);
282 if (gui
->shift_is_pressed ())
284 LayerTypePtr layer
= (LayerTypePtr
) ptr1
;
285 __r_dump_tree (layer
->arc_tree
->root
, 0);
289 Arc
= (ArcTypePtr
) ptr2
;
290 box
= GetArcEnds (Arc
);
292 sprintf (&report
[0], "ARC ID# %ld Flags:%s\n"
293 "CenterPoint(X,Y) = (%d, %d)\n"
294 "Radius = %0.2f %s, Thickness = %0.2f %s\n"
295 "Clearance width in polygons = %0.2f %s\n"
296 "StartAngle = %ld degrees, DeltaAngle = %ld degrees\n"
297 "Bounding Box is (%d,%d), (%d,%d)\n"
298 "That makes the end points at (%d,%d) and (%d,%d)\n"
299 "It is on layer %d\n"
300 "%s", Arc
->ID
, flags_to_string (Arc
->Flags
, ARC_TYPE
),
301 Arc
->X
, Arc
->Y
, UNIT (Arc
->Width
), UNIT (Arc
->Thickness
),
302 UNIT (Arc
->Clearance
/ 2.), Arc
->StartAngle
, Arc
->Delta
,
303 Arc
->BoundingBox
.X1
, Arc
->BoundingBox
.Y1
,
304 Arc
->BoundingBox
.X2
, Arc
->BoundingBox
.Y2
, box
->X1
,
305 box
->Y1
, box
->X2
, box
->Y2
, GetLayerNumber (PCB
->Data
,
308 TEST_FLAG (LOCKFLAG
, Arc
) ? "It is LOCKED\n" : "");
313 PolygonTypePtr Polygon
;
315 if (gui
->shift_is_pressed ())
317 LayerTypePtr layer
= (LayerTypePtr
) ptr1
;
318 __r_dump_tree (layer
->polygon_tree
->root
, 0);
322 Polygon
= (PolygonTypePtr
) ptr2
;
324 sprintf (&report
[0], "POLYGON ID# %ld Flags:%s\n"
325 "Its bounding box is (%d,%d) (%d,%d)\n"
326 "It has %d points and could store %d more\n"
327 "without using more memory.\n"
328 "It resides on layer %d\n"
330 flags_to_string (Polygon
->Flags
, POLYGON_TYPE
),
331 Polygon
->BoundingBox
.X1
, Polygon
->BoundingBox
.Y1
,
332 Polygon
->BoundingBox
.X2
, Polygon
->BoundingBox
.Y2
,
333 Polygon
->PointN
, Polygon
->PointMax
- Polygon
->PointN
,
334 GetLayerNumber (PCB
->Data
, (LayerTypePtr
) ptr1
),
335 TEST_FLAG (LOCKFLAG
, Polygon
) ? "It is LOCKED\n" : "");
340 int len
, dx
, dy
, mgap
;
342 ElementTypePtr element
;
344 if (gui
->shift_is_pressed ())
346 __r_dump_tree (PCB
->Data
->pad_tree
->root
, 0);
350 Pad
= (PadTypePtr
) ptr2
;
351 element
= (ElementTypePtr
) ptr1
;
361 dx
= Pad
->Point1
.X
- Pad
->Point2
.X
;
362 dy
= Pad
->Point1
.Y
- Pad
->Point2
.Y
;
363 len
= sqrt (dx
*dx
+dy
*dy
);
364 mgap
= (Pad
->Mask
- Pad
->Thickness
)/2;
365 sprintf (&report
[0], "PAD ID# %ld Flags:%s\n"
366 "FirstPoint(X,Y) = (%d, %d) ID = %ld\n"
367 "SecondPoint(X,Y) = (%d, %d) ID = %ld\n"
368 "Width = %0.2f %s. Length = %0.2f %s.\n"
369 "Clearance width in polygons = %0.2f %s.\n"
370 "Solder mask = %0.2f x %0.2f %s (gap = %0.2f %s).\n"
372 "It is owned by SMD element %s\n"
373 "As pin number %s and is on the %s\n"
374 "side of the board.\n"
376 flags_to_string (Pad
->Flags
, PAD_TYPE
),
377 Pad
->Point1
.X
, Pad
->Point1
.Y
, Pad
->Point1
.ID
,
378 Pad
->Point2
.X
, Pad
->Point2
.Y
, Pad
->Point2
.ID
,
379 UNIT (Pad
->Thickness
), UNIT (len
+ Pad
->Thickness
),
380 UNIT (Pad
->Clearance
/ 2.),
381 UNIT1 (Pad
->Mask
), UNIT (Pad
->Mask
+ len
), UNIT (mgap
),
383 EMPTY (element
->Name
[1].TextString
),
385 TEST_FLAG (ONSOLDERFLAG
,
386 Pad
) ? "solder (bottom)" : "component",
387 TEST_FLAG (LOCKFLAG
, Pad
) ? "It is LOCKED\n" : "");
392 ElementTypePtr element
;
394 if (gui
->shift_is_pressed ())
396 __r_dump_tree (PCB
->Data
->element_tree
->root
, 0);
400 element
= (ElementTypePtr
) ptr2
;
401 sprintf (&report
[0], "ELEMENT ID# %ld Flags:%s\n"
402 "BoundingBox (%d,%d) (%d,%d)\n"
403 "Descriptive Name \"%s\"\n"
404 "Name on board \"%s\"\n"
405 "Part number name \"%s\"\n"
406 "It is %0.2f %s tall and is located at (X,Y) = (%d,%d)\n"
408 "Mark located at point (X,Y) = (%d,%d)\n"
409 "It is on the %s side of the board.\n"
411 element
->ID
, flags_to_string (element
->Flags
, ELEMENT_TYPE
),
412 element
->BoundingBox
.X1
, element
->BoundingBox
.Y1
,
413 element
->BoundingBox
.X2
, element
->BoundingBox
.Y2
,
414 EMPTY (element
->Name
[0].TextString
),
415 EMPTY (element
->Name
[1].TextString
),
416 EMPTY (element
->Name
[2].TextString
),
417 UNIT (0.45 * element
->Name
[1].Scale
* 100.), element
->Name
[1].X
,
418 element
->Name
[1].Y
, TEST_FLAG (HIDENAMEFLAG
, element
) ?
419 "But it's hidden\n" : "", element
->MarkX
,
420 element
->MarkY
, TEST_FLAG (ONSOLDERFLAG
,
421 element
) ? "solder (bottom)" :
422 "component", TEST_FLAG (LOCKFLAG
, element
) ?
423 "It is LOCKED\n" : "");
428 if (gui
->shift_is_pressed ())
430 LayerTypePtr layer
= (LayerTypePtr
) ptr1
;
431 __r_dump_tree (layer
->text_tree
->root
, 0);
435 case ELEMENTNAME_TYPE
:
440 if (gui
->shift_is_pressed ())
442 __r_dump_tree (PCB
->Data
->name_tree
[NAME_INDEX (PCB
)]->root
, 0);
446 text
= (TextTypePtr
) ptr2
;
448 if (type
== TEXT_TYPE
)
449 sprintf (laynum
, "is on layer %d",
450 GetLayerNumber (PCB
->Data
, (LayerTypePtr
) ptr1
));
451 sprintf (&report
[0], "TEXT ID# %ld Flags:%s\n"
452 "Located at (X,Y) = (%d,%d)\n"
453 "Characters are %0.2f %s tall\n"
456 "The bounding box is (%d,%d) (%d, %d)\n"
458 "%s", text
->ID
, flags_to_string (text
->Flags
, TEXT_TYPE
),
459 text
->X
, text
->Y
, UNIT (0.45 * text
->Scale
* 100.),
460 text
->TextString
, text
->Direction
,
461 text
->BoundingBox
.X1
, text
->BoundingBox
.Y1
,
462 text
->BoundingBox
.X2
, text
->BoundingBox
.Y2
,
463 (type
== TEXT_TYPE
) ? laynum
: "is an element name.",
464 TEST_FLAG (LOCKFLAG
, text
) ? "It is LOCKED\n" : "");
468 case POLYGONPOINT_TYPE
:
470 PointTypePtr point
= (PointTypePtr
) ptr2
;
471 sprintf (&report
[0], "POINT ID# %ld. Points don't have flags.\n"
472 "Located at (X,Y) = (%d,%d)\n"
473 "It belongs to a %s on layer %d\n", point
->ID
,
475 (type
== LINEPOINT_TYPE
) ? "line" : "polygon",
476 GetLayerNumber (PCB
->Data
, (LayerTypePtr
) ptr1
));
484 sprintf (&report
[0], "Unknown\n");
488 if (report
[0] == '\0')
490 Message (_("Nothing found to report on\n"));
493 HideCrosshair (False
);
494 /* create dialog box */
495 gui
->report_dialog ("Report", &report
[0]);
497 RestoreCrosshair (False
);
502 ReportFoundPins (int argc
, char **argv
, int x
, int y
)
504 static DynamicStringType list
;
508 DSClearString (&list
);
509 DSAddString (&list
, "The following pins/pads are FOUND:\n");
510 ELEMENT_LOOP (PCB
->Data
);
514 if (TEST_FLAG (FOUNDFLAG
, pin
))
516 sprintf (temp
, "%s-%s,%c",
517 NAMEONPCB_NAME (element
),
519 ((col
++ % (COLUMNS
+ 1)) == COLUMNS
) ? '\n' : ' ');
520 DSAddString (&list
, temp
);
526 if (TEST_FLAG (FOUNDFLAG
, pad
))
528 sprintf (temp
, "%s-%s,%c",
529 NAMEONPCB_NAME (element
), pad
->Number
,
530 ((col
++ % (COLUMNS
+ 1)) == COLUMNS
) ? '\n' : ' ');
531 DSAddString (&list
, temp
);
538 HideCrosshair (False
);
539 gui
->report_dialog ("Report", list
.Data
);
540 RestoreCrosshair (False
);
545 ReportNetLength (int argc
, char **argv
, int x
, int y
)
551 SaveUndoSerialNumber ();
552 ResetFoundPinsViasAndPads (True
);
553 RestoreUndoSerialNumber ();
554 ResetFoundLinesAndPolygons (True
);
555 RestoreUndoSerialNumber ();
556 gui
->get_coords ("Click on a connection", &x
, &y
);
557 LookupConnection (x
, y
, True
, PCB
->Grid
, FOUNDFLAG
);
559 ALLLINE_LOOP (PCB
->Data
);
561 if (TEST_FLAG (FOUNDFLAG
, line
))
565 dx
= line
->Point1
.X
- line
->Point2
.X
;
566 dy
= line
->Point1
.Y
- line
->Point2
.Y
;
567 l
= sqrt ((double)dx
*dx
+ (double)dy
*dy
);
574 ALLARC_LOOP (PCB
->Data
);
576 if (TEST_FLAG (FOUNDFLAG
, arc
))
579 /* FIXME: we assume width==height here */
580 l
= M_PI
* 2*arc
->Width
* abs(arc
->Delta
)/360.0;
589 gui
->log ("No net under cursor.\n");
593 ELEMENT_LOOP (PCB
->Data
);
597 if (TEST_FLAG (FOUNDFLAG
, pin
))
600 char *ename
= element
->Name
[NAMEONPCB_INDEX
].TextString
;
601 char *pname
= pin
->Number
;
602 char *n
= Concat (ename
, "-", pname
, NULL
);
603 for (ni
= 0; ni
< PCB
->NetlistLib
.MenuN
; ni
++)
604 for (nei
= 0; nei
< PCB
->NetlistLib
.Menu
[ni
].EntryN
; nei
++)
606 if (strcmp (PCB
->NetlistLib
.Menu
[ni
].Entry
[nei
].ListEntry
, n
) == 0)
608 netname
= PCB
->NetlistLib
.Menu
[ni
].Name
+ 2;
609 goto got_net_name
; /* four for loops deep */
617 if (TEST_FLAG (FOUNDFLAG
, pad
))
620 char *ename
= element
->Name
[NAMEONPCB_INDEX
].TextString
;
621 char *pname
= pad
->Number
;
622 char *n
= Concat (ename
, "-", pname
, NULL
);
623 for (ni
= 0; ni
< PCB
->NetlistLib
.MenuN
; ni
++)
624 for (nei
= 0; nei
< PCB
->NetlistLib
.Menu
[ni
].EntryN
; nei
++)
626 if (strcmp (PCB
->NetlistLib
.Menu
[ni
].Entry
[nei
].ListEntry
, n
) == 0)
628 netname
= PCB
->NetlistLib
.Menu
[ni
].Name
+ 2;
629 goto got_net_name
; /* four for loops deep */
639 HideCrosshair (False
);
641 gui
->log ("Net %s length: %0.2f %s\n", netname
, UNIT (length
));
643 gui
->log ("Net length: %0.2f %s\n", UNIT (length
));
644 RestoreCrosshair (False
);
647 /* ---------------------------------------------------------------------------
648 * reports on an object
652 static const char report_syntax
[] = "Report(Object|DrillReport|FoundPins|NetLength)";
654 static const char report_help
[] = "Produce various report.";
656 /* %start-doc actions Report
661 The object under the crosshair will be reported, describing various
662 aspects of the object.
665 A report summarizing the number of drill sizes used, and how many of
666 each, will be produced.
669 A report listing all pins and pads which are marked as ``found'' will
673 The name and length of the net under the crosshair will be reported to
681 Report (int argc
, char **argv
, int x
, int y
)
685 else if (strcasecmp (argv
[0], "Object") == 0)
687 gui
->get_coords ("Click on an object", &x
, &y
);
688 return ReportDialog (argc
- 1, argv
+ 1, x
, y
);
690 else if (strcasecmp (argv
[0], "DrillReport") == 0)
691 return ReportDrills (argc
- 1, argv
+ 1, x
, y
);
692 else if (strcasecmp (argv
[0], "FoundPins") == 0)
693 return ReportFoundPins (argc
- 1, argv
+ 1, x
, y
);
694 else if (strcasecmp (argv
[0], "NetLength") == 0)
695 return ReportNetLength (argc
- 1, argv
+ 1, x
, y
);
701 HID_Action report_action_list
[] = {
702 {"ReportObject", "Click on an object", ReportDialog
,
703 reportdialog_help
, reportdialog_syntax
}
705 {"Report", 0, Report
,
706 report_help
, report_syntax
}
709 REGISTER_ACTIONS (report_action_list
)