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  m_fpCount = 0;
81 
82  if( aTopSide && aBottomSide )
84  else if( aTopSide )
86  else if( aBottomSide )
88  else
90 
91  m_formatCSV = aFormatCSV;
92 }
93 
94 
96 {
97  std::string buffer;
98  char line[1024]; // A line to print intermediate data
99 
100  // Minimal text lengths:
101  m_fpCount = 0;
102  int lenRefText = 8;
103  int lenValText = 8;
104  int lenPkgText = 16;
105 
107 
108  // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
109  m_fpCount = 0;
110 
111  // Select units:
112  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
113  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
114 
115  // Build and sort the list of footprints alphabetically
116  std::vector<LIST_MOD> list;
118 
119  for( auto footprint : m_board->Modules() )
120  {
121  if( m_side != PCB_BOTH_SIDES )
122  {
123  if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
124  continue;
125  if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
126  continue;
127  }
128 
129  if( footprint->GetAttributes() & MOD_VIRTUAL )
130  continue;
131 
132  if( ( footprint->GetAttributes() & MOD_CMS ) == 0 )
133  {
134  if( m_forceSmdItems ) // true to fix a bunch of mis-labeled footprints:
135  {
136  if( !footprint->HasNonSMDPins() )
137  {
138  // all footprint's pins are SMD, mark the part for pick and place
139  // Note: they are not necessary to pick and place,
140  // but the probability is high
141  m_smdFootprintsNotLabeledSMD.push_back( footprint );
142  }
143  else
144  continue;
145  }
146  else
147  continue;
148  }
149 
150  m_fpCount++;
151 
152  LIST_MOD item;
153  item.m_Module = footprint;
154  item.m_Reference = footprint->GetReference();
155  item.m_Value = footprint->GetValue();
156  item.m_Layer = footprint->GetLayer();
157  list.push_back( item );
158 
159  lenRefText = std::max( lenRefText, int(item.m_Reference.length()) );
160  lenValText = std::max( lenValText, int(item.m_Value.length()) );
161  lenPkgText = std::max( lenPkgText, int(item.m_Module->GetFPID().GetLibItemName().length()) );
162  }
163 
164  if( list.size() > 1 )
165  sort( list.begin(), list.end(), sortFPlist );
166 
167  // Switch the locale to standard C (needed to print floating point numbers)
168  LOCALE_IO toggle;
169 
170  if( m_formatCSV )
171  {
172  wxChar csv_sep = ',';
173 
174  // Set first line:;
175  sprintf( line, "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
176  csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
177 
178  buffer += line;
179 
180  for( int ii = 0; ii < m_fpCount; ii++ )
181  {
182  wxPoint footprint_pos;
183  footprint_pos = list[ii].m_Module->GetPosition();
184  footprint_pos -= m_place_Offset;
185 
186  LAYER_NUM layer = list[ii].m_Module->GetLayer();
187  wxASSERT( layer == F_Cu || layer == B_Cu );
188 
189  if( layer == B_Cu )
190  footprint_pos.x = - footprint_pos.x;
191 
192  wxString tmp = "\"" + list[ii].m_Reference;
193  tmp << "\"" << csv_sep;
194  tmp << "\"" << list[ii].m_Value;
195  tmp << "\"" << csv_sep;
196  tmp << "\"" << list[ii].m_Module->GetFPID().GetLibItemName().wx_str();
197  tmp << "\"" << csv_sep;
198 
199  tmp << wxString::Format( "%f%c%f%c%f",
200  footprint_pos.x * conv_unit, csv_sep,
201  // Keep the Y axis oriented from bottom to top,
202  // ( change y coordinate sign )
203  -footprint_pos.y * conv_unit, csv_sep,
204  list[ii].m_Module->GetOrientation() / 10.0 );
205  tmp << csv_sep;
206 
207  tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
209  tmp << '\n';
210 
211  buffer += TO_UTF8( tmp );
212  }
213  }
214  else
215  {
216  // Write file header
217  sprintf( line, "### Module positions - created on %s ###\n", TO_UTF8( DateAndTime() ) );
218 
219  buffer += line;
220 
221  wxString Title = GetBuildVersion();
222  sprintf( line, "### Printed by Pcbnew version %s\n", TO_UTF8( Title ) );
223  buffer += line;
224 
225  buffer += unit_text;
226  buffer += "## Side : ";
227 
228  if( m_side == PCB_BACK_SIDE )
229  buffer += GetBackSideName().c_str();
230  else if( m_side == PCB_FRONT_SIDE )
231  buffer += GetFrontSideName().c_str();
232  else if( m_side == PCB_BOTH_SIDES )
233  buffer += "All";
234  else
235  buffer += "---";
236 
237  buffer += "\n";
238 
239  sprintf(line, "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
240  int(lenRefText), "# Ref",
241  int(lenValText), "Val",
242  int(lenPkgText), "Package",
243  "PosX", "PosY", "Rot", "Side" );
244  buffer += line;
245 
246  for( int ii = 0; ii < m_fpCount; ii++ )
247  {
248  wxPoint footprint_pos;
249  footprint_pos = list[ii].m_Module->GetPosition();
250  footprint_pos -= m_place_Offset;
251 
252  LAYER_NUM layer = list[ii].m_Module->GetLayer();
253  wxASSERT( layer == F_Cu || layer == B_Cu );
254 
255  if( layer == B_Cu )
256  footprint_pos.x = - footprint_pos.x;
257 
258  wxString ref = list[ii].m_Reference;
259  wxString val = list[ii].m_Value;
260  wxString pkg = list[ii].m_Module->GetFPID().GetLibItemName();
261  ref.Replace( wxT( " " ), wxT( "_" ) );
262  val.Replace( wxT( " " ), wxT( "_" ) );
263  pkg.Replace( wxT( " " ), wxT( "_" ) );
264  sprintf(line, "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
265  lenRefText, TO_UTF8( ref ),
266  lenValText, TO_UTF8( val ),
267  lenPkgText, TO_UTF8( pkg ),
268  footprint_pos.x * conv_unit,
269  // Keep the coordinates in the first quadrant,
270  // (i.e. change y sign
271  -footprint_pos.y * conv_unit,
272  list[ii].m_Module->GetOrientation() / 10.0,
273  (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
274  buffer += line;
275  }
276 
277  // Write EOF
278  buffer += "## End\n";
279  }
280 
281  return buffer;
282 }
283 
284 
286 {
287  std::string buffer;
288 
289  m_place_Offset = wxPoint( 0, 0 );
290 
291  // Select units:
292  double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
293  const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
294 
295  LOCALE_IO toggle;
296 
297  // Generate header file comments.)
298  char line[1024];
299  sprintf( line, "## Footprint report - date %s\n", TO_UTF8( DateAndTime() ) );
300  buffer += line;
301 
302  wxString Title = GetBuildVersion();
303  sprintf( line, "## Created by Pcbnew version %s\n", TO_UTF8( Title ) );
304  buffer += line;
305 
306  buffer += unit_text;
307 
308  buffer += "\n$BeginDESCRIPTION\n";
309 
311 
312  buffer += "\n$BOARD\n";
313 
314  sprintf( line, "upper_left_corner %9.6f %9.6f\n",
315  bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
316  buffer += line;
317 
318  sprintf( line, "lower_right_corner %9.6f %9.6f\n",
319  bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
320  buffer += line;
321 
322  buffer += "$EndBOARD\n\n";
323 
324  std::vector<MODULE*> sortedModules;
325 
326  for( MODULE* module : m_board->Modules() )
327  sortedModules.push_back( module );
328 
329  std::sort( sortedModules.begin(), sortedModules.end(),
330  []( MODULE* a, MODULE* b ) -> bool
331  {
332  return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
333  });
334 
335  for( MODULE* module : sortedModules )
336  {
337  sprintf( line, "$MODULE %s\n", EscapedUTF8( module->GetReference() ).c_str() );
338  buffer += line;
339 
340  sprintf( line, "reference %s\n", EscapedUTF8( module->GetReference() ).c_str() );
341  sprintf( line, "value %s\n", EscapedUTF8( module->GetValue() ).c_str() );
342  sprintf( line, "footprint %s\n",
343  EscapedUTF8( FROM_UTF8( module->GetFPID().Format().c_str() ) ).c_str() );
344  buffer += line;
345 
346  buffer += "attribut";
347 
348  if( module->GetAttributes() & MOD_VIRTUAL )
349  buffer += " virtual";
350 
351  if( module->GetAttributes() & MOD_CMS )
352  buffer += " smd";
353 
354  if(( module->GetAttributes() & ( MOD_VIRTUAL | MOD_CMS) ) == 0 )
355  buffer += " none";
356 
357  buffer += "\n";
358 
359  wxPoint module_pos = module->GetPosition();
360  module_pos -= m_place_Offset;
361 
362  sprintf( line, "position %9.6f %9.6f orientation %.2f\n",
363  module_pos.x * conv_unit,
364  module_pos.y * conv_unit,
365  module->GetOrientation() / 10.0 );
366  buffer += line;
367 
368  if( module->GetLayer() == F_Cu )
369  buffer += "layer front\n";
370  else if( module->GetLayer() == B_Cu )
371  buffer += "layer back\n";
372  else
373  buffer += "layer other\n";
374 
375  std::vector<D_PAD*> sortedPads;
376 
377  for( D_PAD* pad : module->Pads() )
378  sortedPads.push_back( pad );
379 
380  std::sort( sortedPads.begin(), sortedPads.end(),
381  []( D_PAD* a, D_PAD* b ) -> bool
382  {
383  return StrNumCmp( a->GetName(), b->GetName(), true ) < 0;
384  });
385 
386  for( D_PAD* pad : sortedPads )
387  {
388  sprintf( line, "$PAD \"%s\"\n", TO_UTF8( pad->GetName() ) );
389  buffer += line;
390 
391  int layer = 0;
392 
393  if( pad->GetLayerSet()[B_Cu] )
394  layer = 1;
395 
396  if( pad->GetLayerSet()[F_Cu] )
397  layer |= 2;
398 
399  static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
400  sprintf( line, "Shape %s Layer %s\n",
401  TO_UTF8( pad->ShowPadShape() ),
402  layer_name[layer] );
403  buffer += line;
404 
405  sprintf( line, "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
406  pad->GetPos0().x * conv_unit,
407  pad->GetPos0().y * conv_unit,
408  pad->GetSize().x * conv_unit,
409  pad->GetSize().y * conv_unit,
410  ( pad->GetOrientation() - module->GetOrientation()) / 10.0 );
411  buffer += line;
412 
413  sprintf( line, "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
414  buffer += line;
415 
416  sprintf( line, "shape_offset %9.6f %9.6f\n",
417  pad->GetOffset().x * conv_unit,
418  pad->GetOffset().y * conv_unit );
419  buffer += line;
420 
421  buffer += "$EndPAD\n";
422  }
423 
424  sprintf( line, "$EndMODULE %s\n\n", TO_UTF8 ( module->GetReference() ) );
425  buffer += line;
426  }
427 
428  // Generate EOF.
429  buffer += "$EndDESCRIPTION\n";
430 
431  return buffer;
432 }
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:382
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:114
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:216
int GetX() const
Definition: eda_rect.h:111
static constexpr double IU_PER_MM
Mock up a conversion function.
Set for modules listed in the automatic insertion list (usually SMD footprints)
Definition: class_module.h:68
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:553
const LIB_ID & GetFPID() const
Definition: class_module.h:225
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:444
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:277
MODULES & Modules()
Definition: class_board.h:266
wxString GetBuildVersion()
Get the full KiCad version string.
static const char unit_text_inch[]
std::string::size_type length() const
Definition: utf8.h:114
int GetRight() const
Definition: eda_rect.h:121
const wxString & GetName() const
Definition: class_pad.h:132
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
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:180
Virtual component: when created by copper shapes on board (Like edge card connectors,...
Definition: class_module.h:70
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
std::vector< MODULE * > m_smdFootprintsNotLabeledSMD
#define IU_PER_MILS
Definition: plotter.cpp:138
int GetY() const
Definition: eda_rect.h:112
#define TO_UTF8(wxstring)
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()
wxPoint m_AuxOrigin
origin for plot exports
wxString DateAndTime()
Definition: string.cpp:373