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 {
226 std::vector<DRILL_LINE_ITEM> drills;
227 CollectDrillLineItems( aBoard, drills );
228
229 aData.drillEntries = std::move( drills );
230 }
231
232 std::sort( aData.drillEntries.begin(), aData.drillEntries.end(),
234
235 aData.minDrillSize = std::numeric_limits<int>::max();
236
237 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
238 {
239 if( drill.shape == PAD_DRILL_SHAPE::CIRCLE )
240 aData.minDrillSize = std::min( aData.minDrillSize, drill.xSize );
241 }
242
243 SHAPE_POLY_SET polySet;
244 aData.hasOutline = aBoard->GetBoardPolygonOutlines( polySet );
245
246 if( aData.hasOutline )
247 {
248 aData.boardArea = 0.0;
249
250 for( int i = 0; i < polySet.OutlineCount(); ++i )
251 {
252 SHAPE_LINE_CHAIN& outline = polySet.Outline( i );
253 aData.boardArea += outline.Area();
254
255 if( aOptions.subtractHolesFromBoardArea )
256 {
257 for( int j = 0; j < polySet.HoleCount( i ); ++j )
258 aData.boardArea -= polySet.Hole( i, j ).Area();
259
260 for( FOOTPRINT* footprint : aBoard->Footprints() )
261 {
262 for( PAD* pad : footprint->Pads() )
263 {
264 if( !pad->HasHole() )
265 continue;
266
267 std::shared_ptr<SHAPE_SEGMENT> hole = pad->GetEffectiveHoleShape();
268
269 if( !hole )
270 continue;
271
272 const SEG& seg = hole->GetSeg();
273 double width = hole->GetWidth();
274 double area = seg.Length() * width;
275
276 area += M_PI * 0.25 * width * width;
277 aData.boardArea -= area;
278 }
279 }
280
281 for( PCB_TRACK* track : aBoard->Tracks() )
282 {
283 if( track->Type() == PCB_VIA_T )
284 {
285 PCB_VIA* via = static_cast<PCB_VIA*>( track );
286 double drill = via->GetDrillValue();
287 aData.boardArea -= M_PI * 0.25 * drill * drill;
288 }
289 }
290 }
291 }
292
293 BOX2I bbox = polySet.BBox();
294
295 aData.boardWidth = static_cast<int>( bbox.GetWidth() );
296 aData.boardHeight = static_cast<int>( bbox.GetHeight() );
297 }
298
299 SHAPE_POLY_SET frontCopper;
300 SHAPE_POLY_SET backCopper;
301 SHAPE_POLY_SET frontHoles;
302 SHAPE_POLY_SET backHoles;
303
304 aBoard->RunOnChildren(
305 [&]( BOARD_ITEM* child )
306 {
307 if( child->Type() == PCB_FOOTPRINT_T || child->Type() == PCB_GROUP_T
308 || child->Type() == PCB_GENERATOR_T )
309 {
310 return;
311 }
312
313 if( child->IsOnLayer( F_Cu ) )
314 child->TransformShapeToPolySet( frontCopper, F_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
315
316 if( child->IsOnLayer( B_Cu ) )
317 child->TransformShapeToPolySet( backCopper, B_Cu, 0, ARC_LOW_DEF, ERROR_INSIDE );
318
319 if( child->Type() == PCB_PAD_T )
320 {
321 PAD* pad = static_cast<PAD*>( child );
322
323 if( pad->HasHole() )
324 {
325 pad->TransformHoleToPolygon( frontHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
326 pad->TransformHoleToPolygon( backHoles, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
327 }
328 }
329 else if( child->Type() == PCB_VIA_T )
330 {
331 PCB_VIA* via = static_cast<PCB_VIA*>( child );
332 VECTOR2I center = via->GetPosition();
333 int radius = via->GetDrillValue() / 2;
334
335 if( via->IsOnLayer( F_Cu ) )
337
338 if( via->IsOnLayer( B_Cu ) )
340 }
341 },
343
344 if( aOptions.subtractHolesFromCopperAreas )
345 {
346 frontCopper.BooleanSubtract( frontHoles );
347 backCopper.BooleanSubtract( backHoles );
348 }
349
350 aData.frontCopperArea = frontCopper.Area();
351 aData.backCopperArea = backCopper.Area();
352
354}
355
356
357static wxString formatCount( int aCount )
358{
359 return wxString::Format( wxT( "%i" ), aCount );
360}
361
362
363static void appendTable( const std::vector<std::vector<wxString>>& aRows, bool aUseFirstColAsLabel, wxString& aOut )
364{
365 if( aRows.empty() )
366 return;
367
368 size_t columnCount = 0;
369
370 for( const std::vector<wxString>& row : aRows )
371 {
372 if( row.size() > columnCount )
373 columnCount = row.size();
374 }
375
376 if( columnCount == 0 )
377 return;
378
379 std::vector<int> widths( columnCount, 0 );
380
381 for( const std::vector<wxString>& row : aRows )
382 {
383 for( size_t col = 0; col < columnCount; ++col )
384 {
385 if( col >= row.size() )
386 continue;
387
388 int cellWidth = static_cast<int>( row[col].length() );
389
390 if( cellWidth > widths[col] )
391 widths[col] = cellWidth;
392 }
393 }
394
395 auto appendDataRow =
396 [&]( const std::vector<wxString>& row, bool treatFirstAsLabel )
397 {
398 if( treatFirstAsLabel && aUseFirstColAsLabel )
399 {
400 wxString formatted;
401 wxString firstColumn;
402
403 if( !row.empty() )
404 firstColumn = row[0];
405
406 formatted.Printf( wxS( "|%-*s |" ), widths[0], firstColumn );
407 aOut << formatted;
408
409 for( size_t col = 1; col < columnCount; ++col )
410 {
411 wxString value;
412 if( col < row.size() )
413 value = row[col];
414
415 formatted.Printf( wxS( " %*s |" ), widths[col], value );
416 aOut << formatted;
417 }
418 }
419 else
420 {
421 aOut << wxS( "|" );
422
423 for( size_t col = 0; col < columnCount; ++col )
424 {
425 wxString value;
426 if( col < row.size() )
427 value = row[col];
428
429 wxString formatted;
430 formatted.Printf( wxS( " %*s |" ), widths[col], value );
431 aOut << formatted;
432 }
433 }
434
435 aOut << wxS( "\n" );
436 };
437
438 appendDataRow( aRows.front(), false );
439
440 aOut << wxS( "|" );
441
442 for( size_t col = 0; col < columnCount; ++col )
443 {
444 int dashCount = widths[col] + 2;
445
446 if( dashCount < 3 )
447 dashCount = 3;
448
449 wxString dashes;
450
451 for( int i = 0; i < dashCount; ++i )
452 dashes << wxS( "-" );
453
454 aOut << dashes << wxS( "|" );
455 }
456
457 aOut << wxS( "\n" );
458
459 for( size_t rowIdx = 1; rowIdx < aRows.size(); ++rowIdx )
460 appendDataRow( aRows[rowIdx], true );
461}
462
463
465 const UNITS_PROVIDER& aUnitsProvider, const wxString& aProjectName,
466 const wxString& aBoardName )
467{
468 wxString report;
469
470 report << _( "PCB statistics report\n=====================" ) << wxS( "\n" );
471 report << wxS( "- " ) << _( "Date" ) << wxS( ": " ) << wxDateTime::Now().Format() << wxS( "\n" );
472 report << wxS( "- " ) << _( "Project" ) << wxS( ": " ) << aProjectName << wxS( "\n" );
473 report << wxS( "- " ) << _( "Board name" ) << wxS( ": " ) << aBoardName << wxS( "\n\n" );
474
475 report << _( "Board" ) << wxS( "\n-----\n" );
476
477 if( aData.hasOutline )
478 {
479 report << wxS( "- " ) << _( "Width" ) << wxS( ": " ) << aUnitsProvider.MessageTextFromValue( aData.boardWidth )
480 << wxS( "\n" );
481 report << wxS( "- " ) << _( "Height" ) << wxS( ": " )
482 << aUnitsProvider.MessageTextFromValue( aData.boardHeight ) << wxS( "\n" );
483 report << wxS( "- " ) << _( "Area" ) << wxS( ": " )
484 << aUnitsProvider.MessageTextFromValue( aData.boardArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
485 }
486 else
487 {
488 report << wxS( "- " ) << _( "Dimensions" ) << wxS( ": " ) << _( "unknown" ) << wxS( "\n" );
489 report << wxS( "- " ) << _( "Area" ) << wxS( ": " ) << _( "unknown" ) << wxS( "\n" );
490 }
491
492 report << wxS( "- " ) << _( "Front copper area" ) << wxS( ": " )
493 << aUnitsProvider.MessageTextFromValue( aData.frontCopperArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
494 report << wxS( "- " ) << _( "Back copper area" ) << wxS( ": " )
495 << aUnitsProvider.MessageTextFromValue( aData.backCopperArea, true, EDA_DATA_TYPE::AREA ) << wxS( "\n" );
496
497 report << wxS( "- " ) << _( "Min track clearance" ) << wxS( ": " )
499 << wxS( "\n" );
500
501 report << wxS( "- " ) << _( "Min track width" ) << wxS( ": " )
502 << aUnitsProvider.MessageTextFromValue( aData.minTrackWidth, true, EDA_DATA_TYPE::DISTANCE ) << wxS( "\n" );
503
504 int minDrill = aData.minDrillSize;
505
506 report << wxS( "- " ) << _( "Min drill diameter" ) << wxS( ": " )
507 << aUnitsProvider.MessageTextFromValue( minDrill, true, EDA_DATA_TYPE::DISTANCE ) << wxS( "\n" );
508
509 report << wxS( "- " ) << _( "Board stackup thickness" ) << wxS( ": " )
510 << aUnitsProvider.MessageTextFromValue( aData.boardThickness, true, EDA_DATA_TYPE::DISTANCE )
511 << wxS( "\n\n" );
512
513 report << _( "Pads" ) << wxS( "\n----\n" );
514
515 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>& padEntry : aData.padEntries )
516 report << wxS( "- " ) << padEntry.title << wxS( " " ) << padEntry.quantity << wxS( "\n" );
517
518 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_PROP>& propEntry : aData.padPropertyEntries )
519 report << wxS( "- " ) << propEntry.title << wxS( " " ) << propEntry.quantity << wxS( "\n" );
520
521 report << wxS( "\n" );
522 report << _( "Vias" ) << wxS( "\n----\n" );
523
524 for( const BOARD_STATISTICS_INFO_ENTRY<VIATYPE>& viaEntry : aData.viaEntries )
525 report << wxS( "- " ) << viaEntry.title << wxS( " " ) << viaEntry.quantity << wxS( "\n" );
526
527 report << wxS( "\n" );
528 report << _( "Components" ) << wxS( "\n----------\n\n" );
529
530 std::vector<std::vector<wxString>> componentRows;
531 std::vector<wxString> header;
532 header.push_back( wxString() );
533 header.push_back( _( "Front Side" ) );
534 header.push_back( _( "Back Side" ) );
535 header.push_back( _( "Total" ) );
536 componentRows.push_back( std::move( header ) );
537
538 int frontTotal = 0;
539 int backTotal = 0;
540
541 for( const BOARD_STATISTICS_FP_ENTRY& fpEntry : aData.footprintEntries )
542 {
543 std::vector<wxString> row;
544 row.push_back( fpEntry.title );
545 row.push_back( formatCount( fpEntry.frontCount ) );
546 row.push_back( formatCount( fpEntry.backCount ) );
547 row.push_back( formatCount( fpEntry.frontCount + fpEntry.backCount ) );
548 componentRows.push_back( std::move( row ) );
549
550 frontTotal += fpEntry.frontCount;
551 backTotal += fpEntry.backCount;
552 }
553
554 std::vector<wxString> totalRow;
555 totalRow.push_back( _( "Total:" ) );
556 totalRow.push_back( formatCount( frontTotal ) );
557 totalRow.push_back( formatCount( backTotal ) );
558 totalRow.push_back( formatCount( frontTotal + backTotal ) );
559 componentRows.push_back( std::move( totalRow ) );
560
561 appendTable( componentRows, true, report );
562
563 report << wxS( "\n" );
564 report << _( "Drill holes" ) << wxS( "\n-----------\n\n" );
565
566 std::vector<std::vector<wxString>> drillRows;
567 std::vector<wxString> drillHeader;
568 drillHeader.push_back( _( "Count" ) );
569 drillHeader.push_back( _( "Shape" ) );
570 drillHeader.push_back( _( "X Size" ) );
571 drillHeader.push_back( _( "Y Size" ) );
572 drillHeader.push_back( _( "Plated" ) );
573 drillHeader.push_back( _( "Via/Pad" ) );
574 drillHeader.push_back( _( "Start Layer" ) );
575 drillHeader.push_back( _( "Stop Layer" ) );
576 drillRows.push_back( std::move( drillHeader ) );
577
578 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
579 {
580 wxString shapeStr;
581
582 switch( drill.shape )
583 {
584 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
585 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
586 default: shapeStr = _( "???" ); break;
587 }
588
589 wxString platedStr = drill.isPlated ? _( "PTH" ) : _( "NPTH" );
590 wxString itemStr = drill.isPad ? _( "Pad" ) : _( "Via" );
591
592 wxString startLayerStr;
593 wxString stopLayerStr;
594
595 if( drill.startLayer == UNDEFINED_LAYER )
596 startLayerStr = _( "N/A" );
597 else if( aBoard )
598 startLayerStr = aBoard->GetLayerName( drill.startLayer );
599 else
600 startLayerStr = _( "N/A" );
601
602 if( drill.stopLayer == UNDEFINED_LAYER )
603 stopLayerStr = _( "N/A" );
604 else if( aBoard )
605 stopLayerStr = aBoard->GetLayerName( drill.stopLayer );
606 else
607 stopLayerStr = _( "N/A" );
608
609 std::vector<wxString> row;
610 row.push_back( formatCount( drill.m_Qty ) );
611 row.push_back( shapeStr );
612 row.push_back( aUnitsProvider.MessageTextFromValue( drill.xSize ) );
613 row.push_back( aUnitsProvider.MessageTextFromValue( drill.ySize ) );
614 row.push_back( platedStr );
615 row.push_back( itemStr );
616 row.push_back( startLayerStr );
617 row.push_back( stopLayerStr );
618 drillRows.push_back( std::move( row ) );
619 }
620
621 appendTable( drillRows, false, report );
622
623 return report;
624}
625
626
628 const UNITS_PROVIDER& aUnitsProvider, const wxString& aProjectName,
629 const wxString& aBoardName )
630{
631 nlohmann::ordered_json root;
632
633 nlohmann::ordered_json metadata;
634 metadata["date"] = GetISO8601CurrentDateTime();
635 metadata["generator"] = "KiCad " + GetBuildVersion();
636 metadata["project"] = aProjectName;
637 metadata["board_name"] = aBoardName;
638 root["metadata"] = metadata;
639
640 nlohmann::ordered_json board;
641 board["has_outline"] = aData.hasOutline;
642
643 if( aData.hasOutline )
644 {
645 board["width"] = aUnitsProvider.MessageTextFromValue( aData.boardWidth );
646 board["height"] = aUnitsProvider.MessageTextFromValue( aData.boardHeight );
647 board["area"] = aUnitsProvider.MessageTextFromValue( aData.boardArea, true, EDA_DATA_TYPE::AREA );
648 }
649 else
650 {
651 board["width"] = nlohmann::json();
652 board["height"] = nlohmann::json();
653 board["area"] = nlohmann::json();
654 }
655
656 board["front_copper_area"] =
657 aUnitsProvider.MessageTextFromValue( aData.frontCopperArea, true, EDA_DATA_TYPE::AREA );
658 board["back_copper_area"] = aUnitsProvider.MessageTextFromValue( aData.backCopperArea, true, EDA_DATA_TYPE::AREA );
659 board["min_track_clearance"] = aUnitsProvider.MessageTextFromValue( aData.minClearanceTrackToTrack );
660 board["min_track_width"] = aUnitsProvider.MessageTextFromValue( aData.minTrackWidth );
661 board["min_drill_diameter"] = aUnitsProvider.MessageTextFromValue( aData.minDrillSize );
662 board["board_thickness"] = aUnitsProvider.MessageTextFromValue( aData.boardThickness );
663 root["board"] = board;
664
665 // The UI strings end in colons, often have a suffix like "via", and need
666 // to be snake_cased
667 auto jsonize =
668 []( const wxString& title, bool removeSuffix ) -> wxString
669 {
670 wxString json = title;
671
672 if( removeSuffix )
673 json = json.BeforeLast( ' ' );
674
675 if( json.EndsWith( wxS( ":" ) ) )
676 json.RemoveLast();
677
678 json.Replace( wxS( " " ), wxS( "_" ) );
679 json.Replace( wxS( "-" ), wxS( "_" ) );
680
681 return json.MakeLower();
682 };
683
684 nlohmann::ordered_json pads = nlohmann::ordered_json::object();
685
686 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_ATTRIB>& padEntry : aData.padEntries )
687 pads[jsonize( padEntry.title, false )] = padEntry.quantity;
688
689 for( const BOARD_STATISTICS_INFO_ENTRY<PAD_PROP>& propEntry : aData.padPropertyEntries )
690 pads[jsonize( propEntry.title, false )] = propEntry.quantity;
691
692 root["pads"] = pads;
693
694 nlohmann::ordered_json vias = nlohmann::ordered_json::object();
695
696 for( const BOARD_STATISTICS_INFO_ENTRY<VIATYPE>& viaEntry : aData.viaEntries )
697 vias[jsonize( viaEntry.title, true )] = viaEntry.quantity;
698
699 root["vias"] = vias;
700
701 int frontTotal = 0;
702 int backTotal = 0;
703 nlohmann::ordered_json components = nlohmann::ordered_json::object();
704
705 for( const BOARD_STATISTICS_FP_ENTRY& fpEntry : aData.footprintEntries )
706 {
707 nlohmann::ordered_json component;
708 component["front"] = fpEntry.frontCount;
709 component["back"] = fpEntry.backCount;
710 component["total"] = fpEntry.frontCount + fpEntry.backCount;
711 components[jsonize( fpEntry.title, false )] = component;
712
713 frontTotal += fpEntry.frontCount;
714 backTotal += fpEntry.backCount;
715 }
716
717 nlohmann::ordered_json totals;
718 totals["front"] = frontTotal;
719 totals["back"] = backTotal;
720 totals["total"] = frontTotal + backTotal;
721 components["total"] = totals;
722
723 root["components"] = components;
724
725 nlohmann::ordered_json drillHoles = nlohmann::ordered_json::array();
726
727 for( const DRILL_LINE_ITEM& drill : aData.drillEntries )
728 {
729 nlohmann::ordered_json drillJson;
730
731 wxString shapeStr;
732
733 switch( drill.shape )
734 {
735 case PAD_DRILL_SHAPE::CIRCLE: shapeStr = _( "Round" ); break;
736 case PAD_DRILL_SHAPE::OBLONG: shapeStr = _( "Slot" ); break;
737 default: shapeStr = _( "???" ); break;
738 }
739
740 wxString sourceStr = drill.isPad ? _( "Pad" ) : _( "Via" );
741 wxString startLayerStr = _( "N/A" );
742 wxString stopLayerStr = _( "N/A" );
743
744 if( aBoard && drill.startLayer != UNDEFINED_LAYER )
745 startLayerStr = aBoard->GetLayerName( drill.startLayer );
746
747 if( aBoard && drill.stopLayer != UNDEFINED_LAYER )
748 stopLayerStr = aBoard->GetLayerName( drill.stopLayer );
749
750 drillJson["count"] = drill.m_Qty;
751 drillJson["shape"] = shapeStr;
752 drillJson["x_size"] = aUnitsProvider.MessageTextFromValue( drill.xSize );
753 drillJson["y_size"] = aUnitsProvider.MessageTextFromValue( drill.ySize );
754 drillJson["plated"] = drill.isPlated;
755 drillJson["source"] = sourceStr;
756 drillJson["start_layer"] = startLayerStr;
757 drillJson["stop_layer"] = stopLayerStr;
758 drillHoles.push_back( std::move( drillJson ) );
759 }
760
761 root["drill_holes"] = drillHoles;
762
763 std::string jsonText = root.dump( 2 );
764 return wxString::FromUTF8( jsonText.c_str() );
765}
@ 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:2645
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:2790
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:695
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition board.cpp:603
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