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