KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_stackup_reporter.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 (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2026 Krishna Swaroop <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
25
26#include "wx/string.h"
27#include "wx/sstream.h"
28
29#include <base_units.h>
30#include <io/csv.h>
31#include <locale_io.h>
32#include <vector>
33
34#include "board_stackup.h"
36
37
38static wxString layerDisplayName( const BOARD_STACKUP_ITEM* aItem )
39{
40 if( aItem->GetType() == BS_ITEM_TYPE_DIELECTRIC )
41 return aItem->FormatDielectricLayerName();
42
43 return aItem->GetLayerName();
44}
45
46
47static void appendCsvProperties( std::vector<std::vector<wxString>>& aRows, BOARD_STACKUP& aStackup,
48 const STACKUP_CSV_OPTIONS& aOptions )
49{
50 auto appendProperty = [&]( const wxString& aName, const wxString& aValue )
51 {
52 aRows.push_back( { aName, aValue } );
53 };
54
55 if( aOptions.includeFinish )
56 appendProperty( wxT( "Finish" ), aStackup.m_FinishType );
57
58 if( aOptions.includeBoardOptions )
59 {
60 if( aStackup.m_HasDielectricConstrains )
61 appendProperty( wxT( "Impedance Controlled" ), wxT( "yes" ) );
62
63 if( aStackup.m_EdgePlating )
64 appendProperty( wxT( "Plated edges" ), wxT( "yes" ) );
65
67 {
68 wxString conn = wxT( "yes" );
69
71 conn << wxT( ",bevelled" );
72
73 appendProperty( wxT( "EdgeConnector" ), conn );
74 }
75 }
76}
77
78
79wxString BuildStackupReport( BOARD_STACKUP& aStackup, EDA_UNITS aUnits )
80{
81 // Build a ascii representation of stackup and copy it in the clipboard
82 wxString report;
83
84 wxString txt;
85 LOCALE_IO toggle; // toggles on the C locale to write floating values, then off.
86
87 for( const BOARD_STACKUP_ITEM* item : aStackup.GetList() )
88 {
89 // Skip stackup items useless for the current board
90 if( !item->IsEnabled() )
91 continue;
92
93 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
94 {
95 wxString sublayer_text;
96
97 if( item->GetSublayersCount() )
98 {
99 sublayer_text.Printf( wxT( "\n sublayer \"1/%d\"" ),
100 item->GetSublayersCount() );
101 }
102
103 txt.Printf( wxT( "layer \"%s\" type \"%s\"%s" ),
104 item->FormatDielectricLayerName(),
105 item->GetTypeName(),
106 sublayer_text );
107 }
108 else
109 {
110 txt.Printf( wxT( "layer \"%s\" type \"%s\"" ),
111 item->GetLayerName(),
112 item->GetTypeName() );
113 }
114
115 report << txt;
116
117 for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
118 {
119 if( idx ) // not printed for the main (first) layer.
120 {
121 txt.Printf( wxT( "\n sublayer \"%d/%d\"" ), idx+1, item->GetSublayersCount() );
122 report << txt;
123 }
124
125 if( item->IsColorEditable() )
126 {
127 txt.Printf( wxT( " Color \"%s\"" ), item->GetColor( idx ) );
128 report << txt;
129 }
130
131 if( item->IsThicknessEditable() )
132 {
133 txt.Printf( wxT( " Thickness %s" ),
134 EDA_UNIT_UTILS::UI::StringFromValue( pcbIUScale, aUnits, item->GetThickness( idx ), true ) );
135 report << txt;
136
137 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
138 {
139 txt.Printf( wxT( " Locked" ) );
140 report << txt;
141 }
142 }
143
144 if( item->IsMaterialEditable() )
145 {
146 txt.Printf( wxT( " Material \"%s\"" ), item->GetMaterial( idx ) );
147 report << txt;
148 }
149
150 if( item->HasEpsilonRValue() )
151 {
152 txt.Printf( wxT( " EpsilonR %s" ), item->FormatEpsilonR( idx ) );
153 report << txt;
154 }
155
156 if( item->HasLossTangentValue() )
157 {
158 txt.Printf( wxT( " LossTg %s" ), item->FormatLossTangent( idx ) );
159 report << txt;
160 }
161 }
162
163 report << '\n';
164 }
165
166 // Finish and other options:
167 txt.Printf( wxT( "Finish \"%s\"" ), aStackup.m_FinishType );
168 report << txt;
169
170 if( aStackup.m_HasDielectricConstrains )
171 report << wxT( " Option \"Impedance Controlled\"" );
172
173 if( aStackup.m_EdgePlating )
174 report << wxT( " Option \"Plated edges\"" );
175
177 {
178 wxString conn_txt = wxT( "yes" );
179
181 conn_txt << wxT( ",bevelled" );
182
183 txt.Printf( wxT( " EdgeConnector \"%s\"" ), conn_txt );
184 report << txt;
185 }
186
187 report << '\n';
188
189 return report;
190}
191
192
193wxString BuildStackupCsv( BOARD_STACKUP& aStackup, EDA_UNITS aUnits, const STACKUP_CSV_OPTIONS& aOptions )
194{
195 std::vector<std::vector<wxString>> rows;
196 LOCALE_IO toggle;
197
198 std::vector<wxString> header = { wxT( "Layer" ), wxT( "Type" ), wxT( "Sublayer" ) };
199
200 if( aOptions.includeThickness )
201 {
202 header.push_back( wxT( "Thickness" ) );
203 header.push_back( wxT( "Locked" ) );
204 }
205
206 if( aOptions.includeMaterial )
207 header.push_back( wxT( "Material" ) );
208
209 if( aOptions.includeColor )
210 header.push_back( wxT( "Color" ) );
211
212 if( aOptions.includeEpsilonR )
213 header.push_back( wxT( "EpsilonR" ) );
214
215 if( aOptions.includeLossTangent )
216 header.push_back( wxT( "LossTg" ) );
217
218 rows.push_back( header );
219
220 for( const BOARD_STACKUP_ITEM* item : aStackup.GetList() )
221 {
222 if( !item->IsEnabled() )
223 continue;
224
225 int sublayerCount = item->GetSublayersCount();
226
227 if( sublayerCount <= 0 )
228 sublayerCount = 1;
229
230 for( int idx = 0; idx < sublayerCount; idx++ )
231 {
232 const bool hasSublayerData = item->GetSublayersCount() > 0;
233 std::vector<wxString> row;
234
235 row.push_back( layerDisplayName( item ) );
236 row.push_back( item->GetTypeName() );
237
238 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->GetSublayersCount() > 1 )
239 row.push_back( wxString::Format( wxT( "%d/%d" ), idx + 1, item->GetSublayersCount() ) );
240 else
241 row.push_back( wxEmptyString );
242
243 if( aOptions.includeThickness )
244 {
245 wxString thickness;
246
247 if( item->IsThicknessEditable() && hasSublayerData )
248 {
249 thickness =
250 EDA_UNIT_UTILS::UI::StringFromValue( pcbIUScale, aUnits, item->GetThickness( idx ), true );
251 }
252
253 row.push_back( thickness );
254
255 const bool locked =
256 item->GetType() == BS_ITEM_TYPE_DIELECTRIC && hasSublayerData && item->IsThicknessLocked( idx );
257 row.push_back( locked ? wxT( "yes" ) : wxEmptyString );
258 }
259
260 if( aOptions.includeMaterial )
261 {
262 row.push_back( ( item->IsMaterialEditable() && hasSublayerData ) ? item->GetMaterial( idx )
263 : wxString() );
264 }
265
266 if( aOptions.includeColor )
267 row.push_back( item->IsColorEditable() ? item->GetColor() : wxString() );
268
269 if( aOptions.includeEpsilonR )
270 {
271 row.push_back( ( item->HasEpsilonRValue() && hasSublayerData ) ? item->FormatEpsilonR( idx )
272 : wxString() );
273 }
274
275 if( aOptions.includeLossTangent )
276 {
277 row.push_back( ( item->HasLossTangentValue() && hasSublayerData ) ? item->FormatLossTangent( idx )
278 : wxString() );
279 }
280
281 rows.push_back( row );
282 }
283 }
284
285 if( aOptions.includeFinish || aOptions.includeBoardOptions )
286 {
287 rows.push_back( {} );
288 rows.push_back( { wxT( "Property" ), wxT( "Value" ) } );
289 appendCsvProperties( rows, aStackup, aOptions );
290 }
291
292 wxStringOutputStream os;
293 CSV_WRITER writer( os );
294 writer.WriteLines( rows );
295
296 return os.GetString();
297}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
@ BS_EDGE_CONNECTOR_BEVELLED
@ BS_EDGE_CONNECTOR_NONE
@ BS_ITEM_TYPE_DIELECTRIC
wxString BuildStackupCsv(BOARD_STACKUP &aStackup, EDA_UNITS aUnits, const STACKUP_CSV_OPTIONS &aOptions)
wxString BuildStackupReport(BOARD_STACKUP &aStackup, EDA_UNITS aUnits)
static wxString layerDisplayName(const BOARD_STACKUP_ITEM *aItem)
static void appendCsvProperties(std::vector< std::vector< wxString > > &aRows, BOARD_STACKUP &aStackup, const STACKUP_CSV_OPTIONS &aOptions)
Manage one layer needed to make a physical board.
wxString FormatDielectricLayerName() const
wxString GetLayerName() const
BOARD_STACKUP_ITEM_TYPE GetType() const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
bool m_EdgePlating
True if the edge board is plated.
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
wxString m_FinishType
The name of external copper finish.
void WriteLines(const std::vector< std::vector< wxString > > &aRows)
Write a vector of rows to the stream.
Definition csv.cpp:18
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
EDA_UNITS
Definition eda_units.h:44
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
Options controlling which stackup fields are included in CSV exports.