KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_statistics_report.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21#include "build_version.h"
22
23#include <board.h>
24#include <footprint.h>
25#include <pad.h>
26#include <pcb_track.h>
30#include <i18n_utility.h>
31#include <nlohmann/json.hpp>
32#include <string_utils.h>
33
34#include <algorithm>
35#include <cmath>
36#include <limits>
37#include <memory>
38#include <wx/datetime.h>
39
41 hasOutline( false ),
42 boardWidth( 0 ),
43 boardHeight( 0 ),
44 boardArea( 0.0 ),
45 frontCopperArea( 0.0 ),
46 backCopperArea( 0.0 ),
47 minClearanceTrackToTrack( std::numeric_limits<int>::max() ),
48 minTrackWidth( std::numeric_limits<int>::max() ),
49 minDrillSize( std::numeric_limits<int>::max() ),
50 boardThickness( 0 ),
52 padEntries(),
54 viaEntries(),
56{
57}
58
59
61{
62 hasOutline = false;
63 boardWidth = 0;
64 boardHeight = 0;
65 boardArea = 0.0;
66 frontCopperArea = 0.0;
67 backCopperArea = 0.0;
68 minClearanceTrackToTrack = std::numeric_limits<int>::max();
69 minTrackWidth = std::numeric_limits<int>::max();
70 minDrillSize = std::numeric_limits<int>::max();
72
74 {
75 fp.frontCount = 0;
76 fp.backCount = 0;
77 }
78
80 pad.quantity = 0;
81
83 prop.quantity = 0;
84
86 via.quantity = 0;
87
88 drillEntries.clear();
89}
90
91
93{
94 aData.footprintEntries.clear();
95 aData.padEntries.clear();
96 aData.padPropertyEntries.clear();
97 aData.viaEntries.clear();
98 aData.drillEntries.clear();
99
101 aData.footprintEntries.push_back( BOARD_STATISTICS_FP_ENTRY( FP_SMD, FP_SMD, _( "SMD:" ) ) );
102 aData.footprintEntries.push_back( BOARD_STATISTICS_FP_ENTRY( FP_THROUGH_HOLE | FP_SMD, 0, _( "Unspecified:" ) ) );
103
104 aData.padEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>( PAD_ATTRIB::PTH, _( "Through hole:" ) ) );
106 aData.padEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>( PAD_ATTRIB::CONN, _( "Connector:" ) ) );
108
109 aData.padPropertyEntries.push_back(
111 aData.padPropertyEntries.push_back(
113
114 aData.viaEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<VIATYPE>( VIATYPE::THROUGH, _( "Through vias:" ) ) );
115 aData.viaEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<VIATYPE>( VIATYPE::BLIND, _( "Blind vias:" ) ) );
116 aData.viaEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<VIATYPE>( VIATYPE::BURIED, _( "Buried vias:" ) ) );
117 aData.viaEntries.push_back( BOARD_STATISTICS_INFO_ENTRY<VIATYPE>( VIATYPE::MICROVIA, _( "Micro vias:" ) ) );
118
119 aData.ResetCounts();
120}
121
122
123static void updatePadCounts( FOOTPRINT* aFootprint, BOARD_STATISTICS_DATA& aData )
124{
125 for( PAD* pad : aFootprint->Pads() )
126 {
128 {
129 if( pad->GetAttribute() == padEntry.attribute )
130 {
131 padEntry.quantity++;
132 break;
133 }
134 }
135
137 {
138 if( pad->GetProperty() == propEntry.attribute )
139 {
140 propEntry.quantity++;
141 break;
142 }
143 }
144 }
145}
146
147
149{
150 aData.ResetCounts();
151
152 if( !aBoard )
153 return;
154
155 static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T };
156
157 for( FOOTPRINT* footprint : aBoard->Footprints() )
158 {
159 if( aOptions.excludeFootprintsWithoutPads && footprint->Pads().empty() )
160 continue;
161
162 for( BOARD_STATISTICS_FP_ENTRY& entry : aData.footprintEntries )
163 {
164 if( ( footprint->GetAttributes() & entry.attributeMask ) == entry.attributeValue )
165 {
166 switch( footprint->GetSide() )
167 {
168 case F_Cu: entry.frontCount++; break;
169 case B_Cu: entry.backCount++; break;
170 default: break;
171 }
172
173 break;
174 }
175 }
176
177 updatePadCounts( footprint, aData );
178 }
179
180 for( PCB_TRACK* track : aBoard->Tracks() )
181 {
182 if( track->Type() == PCB_TRACE_T )
183 aData.minTrackWidth = std::min( aData.minTrackWidth, track->GetWidth() );
184
185 if( !track->IsType( trackTypes ) )
186 continue;
187
188 PCB_LAYER_ID layer = track->GetLayer();
189 auto trackShapeA = track->GetEffectiveShape( layer );
190
191 for( PCB_TRACK* otherTrack : aBoard->Tracks() )
192 {
193 if( layer != otherTrack->GetLayer() )
194 continue;
195
196 if( track->GetNetCode() == otherTrack->GetNetCode() )
197 continue;
198
199 if( !otherTrack->IsType( trackTypes ) )
200 continue;
201
202 int actual = 0;
203 auto trackShapeB = otherTrack->GetEffectiveShape( layer );
204 bool collide = trackShapeA->Collide( trackShapeB.get(), aData.minClearanceTrackToTrack, &actual );
205
206 if( collide )
208 }
209
210 if( track->Type() == PCB_VIA_T )
211 {
212 PCB_VIA* via = static_cast<PCB_VIA*>( track );
213
215 {
216 if( via->GetViaType() == entry.attribute )
217 {
218 entry.quantity++;
219 break;
220 }
221 }
222 }
223 }
224
225 std::vector<DRILL_LINE_ITEM> drills;
226 CollectDrillLineItems( aBoard, drills );
227
228 aData.drillEntries = drills;
229
230 std::sort( aData.drillEntries.begin(), aData.drillEntries.end(),
232
233 aData.minDrillSize = std::numeric_limits<int>::max();
234
235 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
236 {
237 if( drill.shape == PAD_DRILL_SHAPE::CIRCLE )
238 aData.minDrillSize = std::min( aData.minDrillSize, drill.xSize );
239 }
240
241 SHAPE_POLY_SET polySet;
242 aData.hasOutline = aBoard->GetBoardPolygonOutlines( polySet );
243
244 if( aData.hasOutline )
245 {
246 aData.boardArea = 0.0;
247
248 for( int i = 0; i < polySet.OutlineCount(); ++i )
249 {
250 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
251 aData.boardArea += outline.Area();
252
253 if( aOptions.subtractHolesFromBoardArea )
254 {
255 for( int j = 0; j < polySet.HoleCount( i ); ++j )
256 aData.boardArea -= polySet.Hole( i, j ).Area();
257
258 for( FOOTPRINT* footprint : aBoard->Footprints() )
259 {
260 for( PAD* pad : footprint->Pads() )
261 {
262 if( !pad->HasHole() )
263 continue;
264
265 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
266
267 if( !hole )
268 continue;
269
270 const SEG& seg = hole->GetSeg();
271 double width = hole->GetWidth();
272 double area = seg.Length() * width;
273
274 area += M_PI * 0.25 * width * width;
275 aData.boardArea -= area;
276 }
277 }
278
279 for( PCB_TRACK* track : aBoard->Tracks() )
280 {
281 if( track->Type() == PCB_VIA_T )
282 {
283 PCB_VIA* via = static_cast<PCB_VIA*>( track );
284 double drill = via->GetDrillValue();
285 aData.boardArea -= M_PI * 0.25 * drill * drill;
286 }
287 }
288 }
289 }
290
291 BOX2I bbox = polySet.BBox();
292
293 aData.boardWidth = static_cast<int>( bbox.GetWidth() );
294 aData.boardHeight = static_cast<int>( bbox.GetHeight() );
295 }
296
297 SHAPE_POLY_SET frontCopper;
298 SHAPE_POLY_SET backCopper;
299 SHAPE_POLY_SET frontHoles;
300 SHAPE_POLY_SET backHoles;
301
302 aBoard->RunOnChildren(
303 [&]( BOARD_ITEM* child )
304 {
305 if( child->Type() == PCB_FOOTPRINT_T || child->Type() == PCB_GROUP_T
306 || child->Type() == PCB_GENERATOR_T )
307 {
308 return;
309 }
310
311 if( child->IsOnLayer( F_Cu ) )
312 child->TransformShapeToPolySet( frontCopper, F_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
313
314 if( child->IsOnLayer( B_Cu ) )
315 child->TransformShapeToPolySet( backCopper, B_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
316
317 if( child->Type() == PCB_PAD_T )
318 {
319 PAD* pad = static_cast<PAD*>( child );
320
321 if( pad->HasHole() )
322 {
323 pad->TransformHoleToPolygon( frontHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
324 pad->TransformHoleToPolygon( backHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
325 }
326 }
327 else if( child->Type() == PCB_VIA_T )
328 {
329 PCB_VIA* via = static_cast<PCB_VIA*>( child );
330 VECTOR2I center = via->GetPosition();
331 int radius = via->GetDrillValue() / 2;
332
333 if( via->IsOnLayer( F_Cu ) )
335
336 if( via->IsOnLayer( B_Cu ) )
338 }
339 },
341
342 if( aOptions.subtractHolesFromCopperAreas )
343 {
344 frontCopper.BooleanSubtract( frontHoles );
345 backCopper.BooleanSubtract( backHoles );
346 }
347
348 aData.frontCopperArea = frontCopper.Area();
349 aData.backCopperArea = backCopper.Area();
350
352}
353
354
355static wxString formatCount( int aCount )
356{
357 return wxString::Format( wxT( "%i" ), aCount );
358}
359
360
361static void appendTable( const std::vector<std::vector<wxString>>& aRows, bool aUseFirstColAsLabel, wxString& aOut )
362{
363 if( aRows.empty() )
364 return;
365
366 size_t columnCount = 0;
367
368 for( const std::vector<wxString>& row : aRows )
369 {
370 if( row.size() > columnCount )
371 columnCount = row.size();
372 }
373
374 if( columnCount == 0 )
375 return;
376
377 std::vector<int> widths( columnCount, 0 );
378
379 for( const std::vector<wxString>& row : aRows )
380 {
381 for( size_t col = 0; col < columnCount; ++col )
382 {
383 if( col >= row.size() )
384 continue;
385
386 int cellWidth = static_cast<int>( row[col].length() );
387
388 if( cellWidth > widths[col] )
389 widths[col] = cellWidth;
390 }
391 }
392
393 auto appendDataRow = [&]( const std::vector<wxString>& row, bool treatFirstAsLabel )
394 {
395 if( treatFirstAsLabel && aUseFirstColAsLabel )
396 {
397 wxString formatted;
398 wxString firstColumn;
399
400 if( !row.empty() )
401 firstColumn = row[0];
402
403 formatted.Printf( wxS( "|%-*s |" ), widths[0], firstColumn );
404 aOut << formatted;
405
406 for( size_t col = 1; col < columnCount; ++col )
407 {
408 wxString value;
409 if( col < row.size() )
410 value = row[col];
411
412 formatted.Printf( wxS( " %*s |" ), widths[col], value );
413 aOut << formatted;
414 }
415 }
416 else
417 {
418 aOut << wxS( "|" );
419
420 for( size_t col = 0; col < columnCount; ++col )
421 {
422 wxString value;
423 if( col < row.size() )
424 value = row[col];
425
426 wxString formatted;
427 formatted.Printf( wxS( " %*s |" ), widths[col], value );
428 aOut << formatted;
429 }
430 }
431
432 aOut << wxS( "\n" );
433 };
434
435 appendDataRow( aRows.front(), false );
436
437 aOut << wxS( "|" );
438
439 for( size_t col = 0; col < columnCount; ++col )
440 {
441 int dashCount = widths[col] + 2;
442
443 if( dashCount < 3 )
444 dashCount = 3;
445
446 wxString dashes;
447
448 for( int i = 0; i < dashCount; ++i )
449 dashes << wxS( "-" );
450
451 aOut << dashes << wxS( "|" );
452 }
453
454 aOut << wxS( "\n" );
455
456 for( size_t rowIdx = 1; rowIdx < aRows.size(); ++rowIdx )
457 appendDataRow( aRows[rowIdx], true );
458}
459
460
462 const UNITS_PROVIDER& aUnitsProvider, const wxString& aProjectName,
463 const wxString& aBoardName )
464{
465 wxString report;
466
467 report << _( "PCB statistics report\n=====================" ) << wxS( "\n" );
468 report << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxS( "\n" );
469 report << wxS( "- " ) << _( "Project" ) << wxS( ": " ) << aProjectName << wxS( "\n" );
470 report << wxS( "- " ) << _( "Board name" ) << wxS( ": " ) << aBoardName << wxS( "\n\n" );
471
472 report << _( "Board" ) << wxS( "\n-----\n" );
473
474 if( aData.hasOutline )
475 {
476 report << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << aUnitsProvider.MessageTextFromValue( aData.boardWidth )
477 << wxS( "\n" );
478 report << wxS( "- " ) << _( "Height" ) << wxS( ": " )
479 << aUnitsProvider.MessageTextFromValue( aData.boardHeight ) << wxS( "\n" );
480 report << wxS( "- " ) << _( "Area" ) << wxS( ": " )
481 << aUnitsProvider.MessageTextFromValue( aData.boardArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
482 }
483 else
484 {
485 report << wxS( "- " ) << _( "Dimensions" ) << wxS( ": " ) << _( "unknown" ) << wxS( "\n" );
486 report << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxS( "\n" );
487 }
488
489 report << wxS( "- " ) << _( "Front copper area" ) << wxS( ": " )
490 << aUnitsProvider.MessageTextFromValue( aData.frontCopperArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
491 report << wxS( "- " ) << _( "Back copper area" ) << wxS( ": " )
492 << aUnitsProvider.MessageTextFromValue( aData.backCopperArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
493
494 report << wxS( "- " ) << _( "Min track clearance" ) << wxS( ": " )
496 << wxS( "\n" );
497
498 report << wxS( "- " ) << _( "Min track width" ) << wxS( ": " )
499 << aUnitsProvider.MessageTextFromValue( aData.minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) << wxS( "\n" );
500
501 int minDrill = aData.minDrillSize;
502
503 report << wxS( "- " ) << _( "Min drill diameter" ) << wxS( ": " )
504 << aUnitsProvider.MessageTextFromValue( minDrill, true, EDA_DATA_TYPE::DISTANCE ) << wxS( "\n" );
505
506 report << wxS( "- " ) << _( "Board stackup thickness" ) << wxS( ": " )
507 << aUnitsProvider.MessageTextFromValue( aData.boardThickness, true, EDA_DATA_TYPE::DISTANCE )
508 << wxS( "\n\n" );
509
510 report << _( "Pads" ) << wxS( "\n----\n" );
511
512 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>& padEntry : aData.padEntries )
513 report << wxS( "- " ) << padEntry.title << wxS( " " ) << padEntry.quantity << wxS( "\n" );
514
515 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_PROP>& propEntry : aData.padPropertyEntries )
516 report << wxS( "- " ) << propEntry.title << wxS( " " ) << propEntry.quantity << wxS( "\n" );
517
518 report << wxS( "\n" );
519 report << _( "Vias" ) << wxS( "\n----\n" );
520
521 for( const BOARD_STATISTICS_INFO_ENTRY<VIATYPE>& viaEntry : aData.viaEntries )
522 report << wxS( "- " ) << viaEntry.title << wxS( " " ) << viaEntry.quantity << wxS( "\n" );
523
524 report << wxS( "\n" );
525 report << _( "Components" ) << wxS( "\n----------\n\n" );
526
527 std::vector<std::vector<wxString>> componentRows;
528 std::vector<wxString> header;
529 header.push_back( wxString() );
530 header.push_back( _( "Front Side" ) );
531 header.push_back( _( "Back Side" ) );
532 header.push_back( _( "Total" ) );
533 componentRows.push_back( header );
534
535 int frontTotal = 0;
536 int backTotal = 0;
537
538 for( const BOARD_STATISTICS_FP_ENTRY& fpEntry : aData.footprintEntries )
539 {
540 std::vector<wxString> row;
541 row.push_back( fpEntry.title );
542 row.push_back( formatCount( fpEntry.frontCount ) );
543 row.push_back( formatCount( fpEntry.backCount ) );
544 row.push_back( formatCount( fpEntry.frontCount + fpEntry.backCount ) );
545 componentRows.push_back( row );
546
547 frontTotal += fpEntry.frontCount;
548 backTotal += fpEntry.backCount;
549 }
550
551 std::vector<wxString> totalRow;
552 totalRow.push_back( _( "Total:" ) );
553 totalRow.push_back( formatCount( frontTotal ) );
554 totalRow.push_back( formatCount( backTotal ) );
555 totalRow.push_back( formatCount( frontTotal + backTotal ) );
556 componentRows.push_back( totalRow );
557
558 appendTable( componentRows, true, report );
559
560 report << wxS( "\n" );
561 report << _( "Drill holes" ) << wxS( "\n-----------\n\n" );
562
563 std::vector<std::vector<wxString>> drillRows;
564 std::vector<wxString> drillHeader;
565 drillHeader.push_back( _( "Count" ) );
566 drillHeader.push_back( _( "Shape" ) );
567 drillHeader.push_back( _( "X Size" ) );
568 drillHeader.push_back( _( "Y Size" ) );
569 drillHeader.push_back( _( "Plated" ) );
570 drillHeader.push_back( _( "Via/Pad" ) );
571 drillHeader.push_back( _( "Start Layer" ) );
572 drillHeader.push_back( _( "Stop Layer" ) );
573 drillRows.push_back( drillHeader );
574
575 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
576 {
577 wxString shapeStr;
578
579 switch( drill.shape )
580 {
581 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
582 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
583 default: shapeStr = _( "???" ); break;
584 }
585
586 wxString platedStr = drill.isPlated ? _( "PTH" ) : _( "NPTH" );
587 wxString itemStr = drill.isPad ? _( "Pad" ) : _( "Via" );
588
589 wxString startLayerStr;
590 wxString stopLayerStr;
591
592 if( drill.startLayer == UNDEFINED_LAYER )
593 startLayerStr = _( "N/A" );
594 else if( aBoard )
595 startLayerStr = aBoard->GetLayerName( drill.startLayer );
596 else
597 startLayerStr = _( "N/A" );
598
599 if( drill.stopLayer == UNDEFINED_LAYER )
600 stopLayerStr = _( "N/A" );
601 else if( aBoard )
602 stopLayerStr = aBoard->GetLayerName( drill.stopLayer );
603 else
604 stopLayerStr = _( "N/A" );
605
606 std::vector<wxString> row;
607 row.push_back( formatCount( drill.m_Qty ) );
608 row.push_back( shapeStr );
609 row.push_back( aUnitsProvider.MessageTextFromValue( drill.xSize ) );
610 row.push_back( aUnitsProvider.MessageTextFromValue( drill.ySize ) );
611 row.push_back( platedStr );
612 row.push_back( itemStr );
613 row.push_back( startLayerStr );
614 row.push_back( stopLayerStr );
615 drillRows.push_back( row );
616 }
617
618 appendTable( drillRows, false, report );
619
620 return report;
621}
622
623
625 const UNITS_PROVIDER& aUnitsProvider, const wxString& aProjectName,
626 const wxString& aBoardName )
627{
628 nlohmann::ordered_json root;
629
630 nlohmann::ordered_json metadata;
631 metadata["date"] = GetISO8601CurrentDateTime();
632 metadata["generator"] = "KiCad " + GetBuildVersion();
633 metadata["project"] = aProjectName;
634 metadata["board_name"] = aBoardName;
635 root["metadata"] = metadata;
636
637 nlohmann::ordered_json board;
638 board["has_outline"] = aData.hasOutline;
639
640 if( aData.hasOutline )
641 {
642 board["width"] = aUnitsProvider.MessageTextFromValue( aData.boardWidth );
643 board["height"] = aUnitsProvider.MessageTextFromValue( aData.boardHeight );
644 board["area"] = aUnitsProvider.MessageTextFromValue( aData.boardArea, true, EDA_DATA_TYPE::AREA );
645 }
646 else
647 {
648 board["width"] = nlohmann::json();
649 board["height"] = nlohmann::json();
650 board["area"] = nlohmann::json();
651 }
652
653 board["front_copper_area"] =
654 aUnitsProvider.MessageTextFromValue( aData.frontCopperArea, true, EDA_DATA_TYPE::AREA );
655 board["back_copper_area"] = aUnitsProvider.MessageTextFromValue( aData.backCopperArea, true, EDA_DATA_TYPE::AREA );
656 board["min_track_clearance"] = aUnitsProvider.MessageTextFromValue( aData.minClearanceTrackToTrack );
657 board["min_track_width"] = aUnitsProvider.MessageTextFromValue( aData.minTrackWidth );
658 board["min_drill_diameter"] = aUnitsProvider.MessageTextFromValue( aData.minDrillSize );
659 board["board_thickness"] = aUnitsProvider.MessageTextFromValue( aData.boardThickness );
660 root["board"] = board;
661
662 // The UI strings end in colons, often have a suffix like "via", and need
663 // to be snake_cased
664 auto jsonize = []( const wxString& title, bool removeSuffix )
665 {
666 wxString json = title;
667
668 if( removeSuffix )
669 json = json.BeforeLast( ' ' );
670
671 if( json.EndsWith( wxS( ":" ) ) )
672 json.RemoveLast();
673
674 json.Replace( wxS( " " ), wxS( "_" ) );
675 json.Replace( wxS( "-" ), wxS( "_" ) );
676
677 return json.MakeLower();
678 };
679
680 nlohmann::ordered_json pads = nlohmann::ordered_json::object();
681
682 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>& padEntry : aData.padEntries )
683 pads[jsonize( padEntry.title, false )] = padEntry.quantity;
684
685 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_PROP>& propEntry : aData.padPropertyEntries )
686 pads[jsonize( propEntry.title, false )] = propEntry.quantity;
687
688 root["pads"] = pads;
689
690 nlohmann::ordered_json vias = nlohmann::ordered_json::object();
691
692 for( const BOARD_STATISTICS_INFO_ENTRY<VIATYPE>& viaEntry : aData.viaEntries )
693 vias[jsonize( viaEntry.title, true )] = viaEntry.quantity;
694
695 root["vias"] = vias;
696
697 int frontTotal = 0;
698 int backTotal = 0;
699 nlohmann::ordered_json components = nlohmann::ordered_json::object();
700
701 for( const BOARD_STATISTICS_FP_ENTRY& fpEntry : aData.footprintEntries )
702 {
703 nlohmann::ordered_json component;
704 component["front"] = fpEntry.frontCount;
705 component["back"] = fpEntry.backCount;
706 component["total"] = fpEntry.frontCount + fpEntry.backCount;
707 components[jsonize( fpEntry.title, false )] = component;
708
709 frontTotal += fpEntry.frontCount;
710 backTotal += fpEntry.backCount;
711 }
712
713 nlohmann::ordered_json totals;
714 totals["front"] = frontTotal;
715 totals["back"] = backTotal;
716 totals["total"] = frontTotal + backTotal;
717 components["total"] = totals;
718
719 root["components"] = components;
720
721 nlohmann::ordered_json drillHoles = nlohmann::ordered_json::array();
722
723 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
724 {
725 nlohmann::ordered_json drillJson;
726
727 wxString shapeStr;
728
729 switch( drill.shape )
730 {
731 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
732 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
733 default: shapeStr = _( "???" ); break;
734 }
735
736 wxString sourceStr = drill.isPad ? _( "Pad" ) : _( "Via" );
737 wxString startLayerStr = _( "N/A" );
738 wxString stopLayerStr = _( "N/A" );
739
740 if( aBoard && drill.startLayer != UNDEFINED_LAYER )
741 startLayerStr = aBoard->GetLayerName( drill.startLayer );
742
743 if( aBoard && drill.stopLayer != UNDEFINED_LAYER )
744 stopLayerStr = aBoard->GetLayerName( drill.stopLayer );
745
746 drillJson["count"] = drill.m_Qty;
747 drillJson["shape"] = shapeStr;
748 drillJson["x_size"] = aUnitsProvider.MessageTextFromValue( drill.xSize );
749 drillJson["y_size"] = aUnitsProvider.MessageTextFromValue( drill.ySize );
750 drillJson["plated"] = drill.isPlated;
751 drillJson["source"] = sourceStr;
752 drillJson["start_layer"] = startLayerStr;
753 drillJson["stop_layer"] = stopLayerStr;
754 drillHoles.push_back( drillJson );
755 }
756
757 root["drill_holes"] = drillHoles;
758
759 std::string jsonText = root.dump( 2 );
760 return wxString::FromUTF8( jsonText.c_str() );
761}
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_LOW_DEF
Definition base_units.h:128
void CollectDrillLineItems(BOARD *board, std::vector< DRILL_LINE_ITEM > &out)
static void updatePadCounts(FOOTPRINT *aFootprint, BOARD_STATISTICS_DATA &aData)
wxString FormatBoardStatisticsJson(const BOARD_STATISTICS_DATA &aData, BOARD *aBoard, const UNITS_PROVIDER &aUnitsProvider, const wxString &aProjectName, const wxString &aBoardName)
void ComputeBoardStatistics(BOARD *aBoard, const BOARD_STATISTICS_OPTIONS &aOptions, BOARD_STATISTICS_DATA &aData)
static wxString formatCount(int aCount)
wxString FormatBoardStatisticsReport(const BOARD_STATISTICS_DATA &aData, BOARD *aBoard, const UNITS_PROVIDER &aUnitsProvider, const wxString &aProjectName, const wxString &aBoardName)
void InitializeBoardStatisticsData(BOARD_STATISTICS_DATA &aData)
static void appendTable(const std::vector< std::vector< wxString > > &aRows, bool aUseFirstColAsLabel, wxString &aOut)
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
wxString GetBuildVersion()
Get the full KiCad version string.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc) const
Convert the item shape to a polyset.
Definition board_item.h:425
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:314
int BuildBoardThicknessFromStackup() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
BOARD_STACKUP GetStackupOrDefault() const
Definition board.cpp:2642
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition board.cpp:2787
const FOOTPRINTS & Footprints() const
Definition board.h:363
const TRACKS & Tracks() const
Definition board.h:361
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:692
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition board.cpp:600
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
std::deque< PAD * > & Pads()
Definition footprint.h:224
Definition pad.h:54
Definition seg.h:42
int Length() const
Return the length (this).
Definition seg.h:343
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
double Area(bool aAbsolute=true) const
Return the area of this chain.
Represent a set of closed polygons.
double Area()
Return the area of this poly set.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
#define _(s)
@ RECURSE
Definition eda_item.h:51
static const std::vector< KICAD_T > trackTypes
@ FP_SMD
Definition footprint.h:82
@ FP_THROUGH_HOLE
Definition footprint.h:81
nlohmann::json json
Definition gerbview.cpp:50
Some functions to handle hotkeys in KiCad.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
STL namespace.
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:83
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:84
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:107
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:105
@ THROUGH
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:71
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
Definition shape_index.h:97
wxString GetISO8601CurrentDateTime()
std::vector< BOARD_STATISTICS_INFO_ENTRY< PAD_ATTRIB > > padEntries
std::vector< BOARD_STATISTICS_FP_ENTRY > footprintEntries
std::vector< BOARD_STATISTICS_INFO_ENTRY< VIATYPE > > viaEntries
std::vector< BOARD_STATISTICS_INFO_ENTRY< PAD_PROP > > padPropertyEntries
std::vector< DRILL_LINE_ITEM > drillEntries
int attributeMask
int attributeValue
int backCount
int frontCount
wxString title
int quantity
wxString title
T attribute
PCB_LAYER_ID stopLayer
PAD_DRILL_SHAPE shape
PCB_LAYER_ID startLayer
VECTOR2I center
int radius
int actual
#define M_PI
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695