Use fmt for the gendrill report writing

This commit is contained in:
Mark Roszko
2025-12-04 20:10:52 -05:00
parent 8c02b80f89
commit 77e42a151b
2 changed files with 129 additions and 93 deletions
+127 -91
View File
@@ -40,6 +40,9 @@
#include <pcb_painter.h>
#include <pcb_shape.h>
#include <locale_io.h>
#include <fmt.h>
#include <wx/ffile.h>
#include <reporter.h>
#include <set>
@@ -1085,108 +1088,140 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
}
bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName, REPORTER* aReporter )
{
FILE_OUTPUTFORMATTER out( aFullFileName );
wxFFile out( aFullFileName, "wb" );
if( !out.IsOpened() )
{
wxString msg = wxString::Format( _( "Error creating drill report file '%s'" ), aFullFileName );
aReporter->Report( msg, RPT_SEVERITY_ERROR );
return false;
}
static const char separator[] = " =============================================================\n";
FILE* outFp = out.fp();
static const char separator[] =
" =============================================================\n";
wxASSERT( m_pcb );
unsigned totalHoleCount;
wxFileName brdFilename( m_pcb->GetFileName() );
unsigned totalHoleCount;
wxFileName brdFilename( m_pcb->GetFileName() );
std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
out.Print( 0, "Drill report for %s\n", TO_UTF8( brdFilename.GetFullName() ) );
out.Print( 0, "Created on %s\n\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
// Output the cu layer stackup, so layer name references make sense.
out.Print( 0, "Copper Layer Stackup:\n" );
out.Print( 0, separator );
int conventional_layer_num = 1;
for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
try
{
out.Print( 0, " L%-2d: %-25s %s\n", conventional_layer_num++, TO_UTF8( m_pcb->GetLayerName( layer ) ),
layerName( layer ).c_str() ); // generic layer name
}
fmt::print( outFp, "Drill report for {}\n", TO_UTF8( brdFilename.GetFullName() ) );
fmt::print( outFp, "Created on {}\n\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
out.Print( 0, "\n\n" );
// Output the cu layer stackup, so layer name references make sense.
fmt::print( outFp, "Copper Layer Stackup:\n" );
fmt::print( outFp, "{}", separator );
/* output hole lists:
* 1 - through holes
* 2 - for partial holes only: by layer starting and ending pair
* 3 - Non Plated through holes
*/
int conventional_layer_num = 1;
bool buildNPTHlist = false; // First pass: build PTH list only
// in this loop are plated only:
for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
{
const DRILL_SPAN& span = hole_sets[pair_ndx];
buildHolesList( span, buildNPTHlist );
if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
{
out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
out.Print( 0, " plated through holes:\n" );
out.Print( 0, separator );
totalHoleCount = printToolSummary( out, false );
out.Print( 0, " Total plated holes count %u\n", totalHoleCount );
fmt::print( outFp, " L{:<2}: {:<25} {}\n", conventional_layer_num++,
TO_UTF8( m_pcb->GetLayerName( layer ) ),
layerName( layer ).c_str() ); // generic layer name
}
else if( span.m_IsBackdrill )
fmt::print( outFp, "\n\n" );
/* output hole lists:
* 1 - through holes
* 2 - for partial holes only: by layer starting and ending pair
* 3 - Non Plated through holes
*/
bool buildNPTHlist = false; // First pass: build PTH list only
// in this loop are plated only:
for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
{
out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
const DRILL_SPAN& span = hole_sets[pair_ndx];
out.Print( 0, " backdrill span: '%s' to '%s':\n",
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillStartLayer() ) ) ),
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillEndLayer() ) ) ) );
buildHolesList( span, buildNPTHlist );
out.Print( 0, separator );
totalHoleCount = printToolSummary( out, false );
out.Print( 0, " Total backdrilled holes count %u\n", totalHoleCount );
if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
{
fmt::print( outFp, "Drill file '{}' contains\n",
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
fmt::print( outFp, " plated through holes:\n" );
fmt::print( outFp, "{}", separator );
totalHoleCount = printToolSummary( outFp, false );
fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
}
else if( span.m_IsBackdrill )
{
fmt::print( outFp, "Drill file '{}' contains\n",
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
fmt::print( outFp, " backdrill span: '{}' to '{}':\n",
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillStartLayer() ) ) ),
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillEndLayer() ) ) ) );
fmt::print( outFp, "{}", separator );
totalHoleCount = printToolSummary( outFp, false );
fmt::print( outFp, " Total backdrilled holes count {}\n", totalHoleCount );
}
else
{
fmt::print( outFp, "Drill file '{}' contains\n",
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
fmt::print( outFp, " holes connecting layer pair: '{} and {}' ({} vias):\n",
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().first ) ) ),
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().second ) ) ),
span.Pair().first == F_Cu || span.Pair().second == B_Cu ? "blind" : "buried" );
fmt::print( outFp, "{}", separator );
totalHoleCount = printToolSummary( outFp, false );
fmt::print( outFp, " Total plated holes count {}\n", totalHoleCount );
}
fmt::print( outFp, "\n\n" );
}
// NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
// or only the NPTH list (which never has vias)
if( !m_merge_PTH_NPTH )
buildNPTHlist = true;
DRILL_SPAN npthSpan( F_Cu, B_Cu, false, buildNPTHlist );
buildHolesList( npthSpan, buildNPTHlist );
// nothing wrong with an empty NPTH file in report.
if( m_merge_PTH_NPTH )
fmt::print( outFp, "Not plated through holes are merged with plated holes\n" );
else
fmt::print( outFp, "Drill file '{}' contains\n",
TO_UTF8( getDrillFileName( npthSpan, true, m_merge_PTH_NPTH ) ) );
fmt::print( outFp, " unplated through holes:\n" );
fmt::print( outFp, "{}", separator );
totalHoleCount = printToolSummary( outFp, true );
fmt::print( outFp, " Total unplated holes count {}\n", totalHoleCount );
}
catch( const fmt::format_error& )
{
if( aReporter != nullptr )
{
out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
out.Print( 0, " holes connecting layer pair: '%s and %s' (%s vias):\n",
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().first ) ) ),
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().second ) ) ),
span.Pair().first == F_Cu || span.Pair().second == B_Cu ? "blind" : "buried" );
out.Print( 0, separator );
totalHoleCount = printToolSummary( out, false );
out.Print( 0, " Total plated holes count %u\n", totalHoleCount );
wxString msg = wxString::Format( _( "Error writing drill report file '%s'" ), aFullFileName);
aReporter->Report( msg, RPT_SEVERITY_ERROR );
}
out.Print( 0, "\n\n" );
return false;
}
// NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
// or only the NPTH list (which never has vias)
if( !m_merge_PTH_NPTH )
buildNPTHlist = true;
DRILL_SPAN npthSpan( F_Cu, B_Cu, false, buildNPTHlist );
buildHolesList( npthSpan, buildNPTHlist );
// nothing wrong with an empty NPTH file in report.
if( m_merge_PTH_NPTH )
out.Print( 0, "Not plated through holes are merged with plated holes\n" );
else
out.Print( 0, "Drill file '%s' contains\n", TO_UTF8( getDrillFileName( npthSpan, true, m_merge_PTH_NPTH ) ) );
out.Print( 0, " unplated through holes:\n" );
out.Print( 0, separator );
totalHoleCount = printToolSummary( out, true );
out.Print( 0, " Total unplated holes count %u\n", totalHoleCount );
if( aReporter != nullptr )
{
wxString msg = wxString::Format( _( "Created drill report file '%s'" ), aFullFileName );
aReporter->Report( msg, RPT_SEVERITY_ACTION );
}
return true;
}
@@ -1218,7 +1253,7 @@ bool GENDRILL_WRITER_BASE::plotDrillMarks( PLOTTER* aPlotter )
}
unsigned GENDRILL_WRITER_BASE::printToolSummary( OUTPUTFORMATTER& out, bool aSummaryNPTH ) const
unsigned GENDRILL_WRITER_BASE::printToolSummary( FILE* out, bool aSummaryNPTH ) const
{
unsigned totalHoleCount = 0;
@@ -1233,34 +1268,35 @@ unsigned GENDRILL_WRITER_BASE::printToolSummary( OUTPUTFORMATTER& out, bool aSum
continue;
// List the tool number assigned to each drill in mm then in inches.
int tool_number = ii + 1;
out.Print( 0, " T%d %2.3fmm %2.4f\" ", tool_number, diameter_in_mm( tool.m_Diameter ),
int tool_number = ii+1;
fmt::print( 0, " T{} {:2.3f}mm {:2.4f}\" ", tool_number,
diameter_in_mm( tool.m_Diameter ),
diameter_in_inches( tool.m_Diameter ) );
// Now list how many holes and ovals are associated with each drill.
if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
out.Print( 0, "(1 hole" );
fmt::print( out, "(1 hole" );
else if( tool.m_TotalCount == 1 )
out.Print( 0, "(1 hole) (with 1 slot" );
fmt::print( out, "(1 hole) (with 1 slot" );
else if( tool.m_OvalCount == 0 )
out.Print( 0, "(%d holes)", tool.m_TotalCount );
fmt::print( out, "({} holes)", tool.m_TotalCount );
else if( tool.m_OvalCount == 1 )
out.Print( 0, "(%d holes) (with 1 slot", tool.m_TotalCount );
fmt::print( out, "({} holes) (with 1 slot", tool.m_TotalCount );
else // tool.m_OvalCount > 1
out.Print( 0, "(%d holes) (with %d slots", tool.m_TotalCount, tool.m_OvalCount );
fmt::print( out, "({} holes) (with {} slots", tool.m_TotalCount, tool.m_OvalCount );
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED )
out.Print( 0, ", castellated" );
fmt::print( out, ", castellated" );
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT )
out.Print( 0, ", press-fit" );
fmt::print( out, ", press-fit" );
out.Print( 0, ")\n" );
fmt::print( out, ")\n" );
totalHoleCount += tool.m_TotalCount;
}
out.Print( 0, "\n" );
fmt::print( out, "\n" );
return totalHoleCount;
}
+2 -2
View File
@@ -407,7 +407,7 @@ public:
* @param aFullFileName is the name of the file to create.
* @return true if the file is created.
*/
bool GenDrillReportFile( const wxString& aFullFileName );
bool GenDrillReportFile( const wxString& aFullFileName, REPORTER* aReporter = nullptr );
/**
* Returns the file extension of the drill writer format
@@ -464,7 +464,7 @@ protected:
* @param aOut is the current OUTPUTFORMATTER to print summary.
* @param aSummaryNPTH is true to print summary for NPTH, false for PTH.
*/
unsigned printToolSummary( OUTPUTFORMATTER& aOut, bool aSummaryNPTH ) const;
unsigned printToolSummary( FILE* out, bool aSummaryNPTH ) const;
/**
* @return a string from aPair to identify the layer layer pair.