KiCad PCB EDA Suite
export_footprints_placefile.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) 2015-2019 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but 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
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 /*
25  * 1 - create ascii/csv files for automatic placement of smd components
26  * 2 - create a module report (pos and module descr) (ascii file)
27  */
28 
29 #include <fctsys.h>
30 #include <kicad_string.h>
31 #include <build_version.h>
32 #include <macros.h>
34 
35 
36 class LIST_MOD // An helper class used to build a list of useful footprints.
37 {
38 public:
39  MODULE* m_Module; // Link to the actual footprint
40  wxString m_Reference; // Its schematic reference
41  wxString m_Value; // Its schematic value
42  LAYER_NUM m_Layer; // its side (B_Cu, or F_Cu)
43 };
44 
45 
46 // Defined values to write coordinates using inches or mm:
47 static const double conv_unit_inch = 0.001 / IU_PER_MILS ; // units = INCHES
48 static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n";
49 
50 static const double conv_unit_mm = 1.0 / IU_PER_MM; // units = mm
51 static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n";
52 
53 // Sort function use by GenereModulesPosition()
54 // sort is made by side (layer) top layer first
55 // then by reference increasing order
56 static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst )
57 {
58  if( ref.m_Layer == tst.m_Layer )
59  return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0;
60 
61  return ref.m_Layer > tst.m_Layer;
62 }
63 
64 
66 {
71 };
72 
74  bool aForceSmdItems, bool aTopSide,
75  bool aBottomSide, bool aFormatCSV )
76 {
77  m_board = aBoard;
78  m_unitsMM = aUnitsMM;
79  m_forceSmdItems = aForceSmdItems;
80 
81  if( aTopSide && aBottomSide )
83  else if( aTopSide )
85  else if( aBottomSide )
87  else
89 
90  m_formatCSV = aFormatCSV;
91 }
92 
93 
95 {
96  std::string buffer;
97  char line[1024]; // A line to print intermediate data
98 
99  // Minimal text lengths:
100  m_fpCount = 0;
101  int lenRefText = 8;
102  int lenValText = 8;
103  int lenPkgText = 16;
104 
106 
107  // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
108  m_fpCount = 0;
109 
110  // Select units:
111  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
112  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
113 
114  // Build and sort the list of footprints alphabetically
115  std::vector<LIST_MOD> list;
117 
118  for( auto footprint : m_board->Modules() )
119  {
120  if( m_side != PCB_BOTH_SIDES )
121  {
122  if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
123  continue;
124  if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
125  continue;
126  }
127 
128  if( footprint->GetAttributes() & MOD_VIRTUAL )
129  continue;
130 
131  if( ( footprint->GetAttributes() & MOD_CMS ) == 0 )
132  {
133  if( m_forceSmdItems ) // true to fix a bunch of mis-labeled footprints:
134  {
135  if( !footprint->HasNonSMDPins() )
136  {
137  // all footprint's pins are SMD, mark the part for pick and place
138  // Note: they are not necessary to pick and place,
139  // but the probability is high
140  m_smdFootprintsNotLabeledSMD.push_back( footprint );
141  }
142  else
143  continue;
144  }
145  else
146  continue;
147  }
148 
149  m_fpCount++;
150 
151  LIST_MOD item;
152  item.m_Module = footprint;
153  item.m_Reference = footprint->GetReference();
154  item.m_Value = footprint->GetValue();
155  item.m_Layer = footprint->GetLayer();
156  list.push_back( item );
157 
158  lenRefText = std::max( lenRefText, int(item.m_Reference.length()) );
159  lenValText = std::max( lenValText, int(item.m_Value.length()) );
160  lenPkgText = std::max( lenPkgText, int(item.m_Module->GetFPID().GetLibItemName().length()) );
161  }
162 
163  if( list.size() > 1 )
164  sort( list.begin(), list.end(), sortFPlist );
165 
166  // Switch the locale to standard C (needed to print floating point numbers)
167  LOCALE_IO toggle;
168 
169  if( m_formatCSV )
170  {
171  wxChar csv_sep = ',';
172 
173  // Set first line:;
174  sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
175  csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
176 
177  buffer += line;
178 
179  for( int ii = 0; ii < m_fpCount; ii++ )
180  {
181  wxPoint footprint_pos;
182  footprint_pos = list[ii].m_Module->GetPosition();
183  footprint_pos -= m_place_Offset;
184 
185  LAYER_NUM layer = list[ii].m_Module->GetLayer();
186  wxASSERT( layer == F_Cu || layer == B_Cu );
187 
188  wxString tmp = "\"" + list[ii].m_Reference;
189  tmp << "\"" << csv_sep;
190  tmp << "\"" << list[ii].m_Value;
191  tmp << "\"" << csv_sep;
192  tmp << "\"" << list[ii].m_Module->GetFPID().GetLibItemName().wx_str();
193  tmp << "\"" << csv_sep;
194 
195  tmp << wxString::Format( "%f%c%f%c%f",
196  footprint_pos.x * conv_unit, csv_sep,
197  // Keep the Y axis oriented from bottom to top,
198  // ( change y coordinate sign )
199  -footprint_pos.y * conv_unit, csv_sep,
200  list[ii].m_Module->GetOrientation() / 10.0 );
201  tmp << csv_sep;
202 
203  tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
205  tmp << '\n';
206 
207  buffer += TO_UTF8( tmp );
208  }
209  }
210  else
211  {
212  // Write file header
213  sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
214 
215  buffer += line;
216 
217  wxString Title = GetBuildVersion();
218  sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) );
219  buffer += line;
220 
221  buffer += unit_text;
222  buffer += "## Side : ";
223 
224  if( m_side == PCB_BACK_SIDE )
225  buffer += GetBackSideName().c_str();
226  else if( m_side == PCB_FRONT_SIDE )
227  buffer += GetFrontSideName().c_str();
228  else if( m_side == PCB_BOTH_SIDES )
229  buffer += "All";
230  else
231  buffer += "---";
232 
233  buffer += "\n";
234 
235  sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
236  int(lenRefText), "# Ref",
237  int(lenValText), "Val",
238  int(lenPkgText), "Package",
239  "PosX", "PosY", "Rot", "Side" );
240  buffer += line;
241 
242  for( int ii = 0; ii < m_fpCount; ii++ )
243  {
244  wxPoint footprint_pos;
245  footprint_pos = list[ii].m_Module->GetPosition();
246  footprint_pos -= m_place_Offset;
247 
248  LAYER_NUM layer = list[ii].m_Module->GetLayer();
249  wxASSERT( layer == F_Cu || layer == B_Cu );
250 
251  if( layer == B_Cu )
252  footprint_pos.x = - footprint_pos.x;
253 
254  wxString ref = list[ii].m_Reference;
255  wxString val = list[ii].m_Value;
256  wxString pkg = list[ii].m_Module->GetFPID().GetLibItemName();
257  ref.Replace( wxT( " " ), wxT( "_" ) );
258  val.Replace( wxT( " " ), wxT( "_" ) );
259  pkg.Replace( wxT( " " ), wxT( "_" ) );
260  sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
261  lenRefText, TO_UTF8( ref ),
262  lenValText, TO_UTF8( val ),
263  lenPkgText, TO_UTF8( pkg ),
264  footprint_pos.x * conv_unit,
265  // Keep the coordinates in the first quadrant,
266  // (i.e. change y sign
267  -footprint_pos.y * conv_unit,
268  list[ii].m_Module->GetOrientation() / 10.0,
269  (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
270  buffer += line;
271  }
272 
273  // Write EOF
274  buffer += "## End\n";
275  }
276 
277  return buffer;
278 }
279 
280 
282 {
283  std::string buffer;
284 
285  m_place_Offset = wxPoint( 0, 0 );
286 
287  // Select units:
288  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
289  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
290 
291  LOCALE_IO toggle;
292 
293  // Generate header file comments.)
294  char line[1024];
295  sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
296  buffer += line;
297 
298  wxString Title = GetBuildVersion();
299  sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) );
300  buffer += line;
301 
302  buffer += unit_text;
303 
304  buffer += "\n$BeginDESCRIPTION\n";
305 
307 
308  buffer += "\n$BOARD\n";
309 
310  sprintf( line, "upper_left_corner %9.6f %9.6f\n",
311  bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
312  buffer += line;
313 
314  sprintf( line, "lower_right_corner %9.6f %9.6f\n",
315  bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
316  buffer += line;
317 
318  buffer += "$EndBOARD\n\n";
319 
320  std::vector<MODULE*> sortedModules;
321 
322  for( MODULE* module : m_board->Modules() )
323  sortedModules.push_back( module );
324 
325  std::sort( sortedModules.begin(), sortedModules.end(),
326  []( MODULE* a, MODULE* b ) -> bool
327  {
328  return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
329  });
330 
331  for( MODULE* module : sortedModules )
332  {
333  sprintf( line, "$MODULE %s\n", EscapedUTF8( module->GetReference() ).c_str() );
334  buffer += line;
335 
336  sprintf( line, "reference %s\n", EscapedUTF8( module->GetReference() ).c_str() );
337  sprintf( line, "value %s\n", EscapedUTF8( module->GetValue() ).c_str() );
338  sprintf( line, "footprint %s\n",
339  EscapedUTF8( FROM_UTF8( module->GetFPID().Format().c_str() ) ).c_str() );
340  buffer += line;
341 
342  buffer += "attribut";
343 
344  if( module->GetAttributes() & MOD_VIRTUAL )
345  buffer += " virtual";
346 
347  if( module->GetAttributes() & MOD_CMS )
348  buffer += " smd";
349 
350  if(( module->GetAttributes() & ( MOD_VIRTUAL | MOD_CMS) ) == 0 )
351  buffer += " none";
352 
353  buffer += "\n";
354 
355  wxPoint module_pos = module->GetPosition();
356  module_pos -= m_place_Offset;
357 
358  sprintf( line, "position %9.6f %9.6f orientation %.2f\n",
359  module_pos.x * conv_unit,
360  module_pos.y * conv_unit,
361  module->GetOrientation() / 10.0 );
362  buffer += line;
363 
364  if( module->GetLayer() == F_Cu )
365  buffer += "layer front\n";
366  else if( module->GetLayer() == B_Cu )
367  buffer += "layer back\n";
368  else
369  buffer += "layer other\n";
370 
371  std::vector<D_PAD*> sortedPads;
372 
373  for( D_PAD* pad : module->Pads() )
374  sortedPads.push_back( pad );
375 
376  std::sort( sortedPads.begin(), sortedPads.end(),
377  []( D_PAD* a, D_PAD* b ) -> bool
378  {
379  return StrNumCmp( a->GetName(), b->GetName(), true ) < 0;
380  });
381 
382  for( D_PAD* pad : sortedPads )
383  {
384  sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) );
385  buffer += line;
386 
387  int layer = 0;
388 
389  if( pad->GetLayerSet()[B_Cu] )
390  layer = 1;
391 
392  if( pad->GetLayerSet()[F_Cu] )
393  layer |= 2;
394 
395  static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
396  sprintf( line, "Shape %s Layer %s\n",
397  TO_UTF8( pad->ShowPadShape() ),
398  layer_name[layer] );
399  buffer += line;
400 
401  sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
402  pad->GetPos0().x * conv_unit,
403  pad->GetPos0().y * conv_unit,
404  pad->GetSize().x * conv_unit,
405  pad->GetSize().y * conv_unit,
406  ( pad->GetOrientation() - module->GetOrientation()) / 10.0 );
407  buffer += line;
408 
409  sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
410  buffer += line;
411 
412  sprintf( line, "shape_offset %9.6f %9.6f\n",
413  pad->GetOffset().x * conv_unit,
414  pad->GetOffset().y * conv_unit );
415  buffer += line;
416 
417  buffer += "$EndPAD\n";
418  }
419 
420  sprintf( line, "$EndMODULE %s\n\n", TO_UTF8 ( module->GetReference() ) );
421  buffer += line;
422  }
423 
424  // Generate EOF.
425  buffer += "$EndDESCRIPTION\n";
426 
427  return buffer;
428 }
std::string GenPositionData()
build a string filled with the position data
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Definition: string.cpp:354
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:62
static const double conv_unit_mm
static const char unit_text_mm[]
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:154
int GetX() const
Definition: eda_rect.h:111
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:77
const LIB_ID & GetFPID() const
Definition: class_module.h:222
PLACE_FILE_EXPORTER(BOARD *aBoard, bool aUnitsMM, bool aForceSmdItems, bool aTopSide, bool aBottomSide, bool aFormatCSV)
Create a PLACE_FILE_EXPORTER.
This file contains miscellaneous commonly used macros and functions.
int GetBottom() const
Definition: eda_rect.h:124
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:432
static const double conv_unit_inch
std::string EscapedUTF8(wxString aString)
Function EscapedUTF8 returns an 8 bit UTF8 string given aString in unicode form.
Definition: string.cpp:249
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:48
MODULES & Modules()
Definition: class_board.h:227
wxString GetBuildVersion()
Function GetBuildVersion Return the build version string.
static const char unit_text_inch[]
std::string::size_type length() const
Definition: utf8.h:114
const wxPoint GetPosition() const
Definition: eda_rect.h:115
int GetRight() const
Definition: eda_rect.h:121
const wxString & GetName() const
Definition: class_pad.h:195
const wxPoint & GetAuxOrigin() const
Definition: class_board.h:354
std::string GenReportData()
build a string filled with the pad report data This report does not used options aForceSmdItems,...
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define max(a, b)
Definition: auxiliary.h:86
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:161
Virtual component: when created by copper shapes on board (Like edge card connectors,...
Definition: class_module.h:79
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
std::vector< MODULE * > m_smdFootprintsNotLabeledSMD
#define IU_PER_MILS
Definition: plotter.cpp:136
int GetY() const
Definition: eda_rect.h:112
static bool sortFPlist(const LIST_MOD &ref, const LIST_MOD &tst)
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Function ComputeBoundingBox calculates the bounding box containing all board items (or board edge seg...
static std::string GetBackSideName()
static std::string GetFrontSideName()
wxString DateAndTime()
Definition: string.cpp:345