KiCad PCB EDA Suite
backannotate.cpp
Go to the documentation of this file.
1 
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 2019 Alexander Shuklin <Jasuramme@gmail.com>
9  * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 
30 #include <backannotate.h>
31 #include <boost/property_tree/ptree.hpp>
32 #include <confirm.h>
33 #include <dsnlexer.h>
34 #include <kiface_i.h>
35 #include <macros.h>
36 #include <ptree.h>
37 #include <reporter.h>
38 #include <sch_edit_frame.h>
39 #include <sch_sheet_path.h>
40 #include <schematic.h>
41 #include <utf8.h>
42 
43 
45  m_settings( aSettings ),
46  m_frame( aFrame ),
47  m_changesCount( 0 )
48 {
49 }
50 
51 
53 {
54 }
55 
56 
57 bool BACK_ANNOTATE::BackAnnotateSymbols( const std::string& aNetlist )
58 {
59  m_changesCount = 0;
60  wxString msg;
61 
64  {
66  _( "Select at least one property to back annotate" ), RPT_SEVERITY_ERROR );
67  return false;
68  }
69 
70  int errors = getPcbModulesFromString( aNetlist );
71 
73  sheets.GetComponents( m_refs, false );
75 
76  errors += getChangeList();
77  errors += checkForUnusedSymbols();
78  errors += checkSharedSchematicErrors();
79 
80  if( errors > 0 )
81  m_settings.dryRun = true;
83 
84  if( !errors )
85  {
86 
87  if( !m_settings.dryRun )
88  {
89  msg.Printf( _( "Schematic is back-annotated. %d changes applied." ), m_changesCount );
91  }
92  else
94  _( "No errors during dry run. Ready to go." ), RPT_SEVERITY_ACTION );
95  }
96  else
97  {
98  msg.Printf( _( "Found %d errors. Fix them and run back annotation." ), errors );
100  }
101 
102  return !errors;
103 }
104 
105 bool BACK_ANNOTATE::FetchNetlistFromPCB( std::string& aNetlist )
106 {
107 
108  if( Kiface().IsSingle() )
109  {
111  m_frame, _( "Cannot fetch PCB netlist because eeschema is opened in "
112  "stand-alone mode.\n"
113  "You must launch the KiCad project manager "
114  "and create a project." ) );
115  return false;
116  }
117 
118  KIWAY_PLAYER* player = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
119 
120  if( !player )
121  {
123  this->m_frame, _( "Please open Pcbnew and run back annotation again" ) );
124  return false;
125  }
126 
128  return true;
129 }
130 
131 
132 int BACK_ANNOTATE::getPcbModulesFromString( const std::string& aPayload )
133 {
134  DSNLEXER lexer( aPayload, FROM_UTF8( __func__ ) );
135  PTREE doc;
136  Scan( &doc, &lexer );
137  CPTREE& tree = doc.get_child( "pcb_netlist" );
138  wxString msg;
139 
140  int errors = 0;
141  m_pcbModules.clear();
142  for( auto& item : tree )
143  {
144  wxString path, value, footprint;
145  wxASSERT( item.first == "ref" );
146  wxString ref = UTF8( item.second.front().first );
147  try
148  {
149  path = UTF8( item.second.get_child( "timestamp" ).front().first );
150 
151  if( path == "" )
152  {
153  msg.Printf( _( "Footprint \"%s\" has no symbol associated." ), ref );
155  continue;
156  }
157  footprint = UTF8( item.second.get_child( "fpid" ).front().first );
158  value = UTF8( item.second.get_child( "value" ).front().first );
159  }
160  catch( ... )
161  {
162  wxLogWarning( "Cannot parse PCB netlist for back-annotation" );
163  }
164 
165  // Use lower_bound for not to iterate over map twice
166  auto nearestItem = m_pcbModules.lower_bound( path );
167 
168  if( nearestItem != m_pcbModules.end() && nearestItem->first == path )
169  {
170  // Module with this path already exists - generate error
171  msg.Printf( _( "Pcb footprints \"%s\" and \"%s\" linked to same symbol" ),
172  nearestItem->second->ref, ref );
174  ++errors;
175  }
176  else
177  {
178  // Add module to the map
179  PCB_MODULE_DATA data( ref, footprint, value );
180  m_pcbModules.insert( nearestItem,
181  std::make_pair( path, std::make_shared<PCB_MODULE_DATA>( data ) ) );
182  }
183  }
184  return errors;
185 }
186 
188 {
189  int errors = 0;
190 
191  for( auto& module : m_pcbModules )
192  {
193  auto& pcbPath = module.first;
194  auto& pcbData = module.second;
195  bool foundInMultiunit = false;
196 
197  for( auto& item : m_multiUnitsRefs )
198  {
199  auto& refList = item.second;
200 
201  if( refList.FindRefByPath( pcbPath ) >= 0 )
202  {
203 
204  // If module linked to multi unit symbol, we add all symbol's units to
205  // the change list
206  foundInMultiunit = true;
207  for( size_t i = 0; i < refList.GetCount(); ++i )
208  {
209  m_changelist.push_back( CHANGELIST_ITEM( refList[i], pcbData ) );
210  }
211  break;
212  }
213  }
214 
215  if( foundInMultiunit )
216  continue;
217 
218  int refIndex = m_refs.FindRefByPath( pcbPath );
219 
220  if( refIndex >= 0 )
221  m_changelist.push_back( CHANGELIST_ITEM( m_refs[refIndex], pcbData ) );
222  else
223  {
224  // Haven't found linked symbol in multiunits or common refs. Generate error
225  wxString msg;
226  msg.Printf( _( "Cannot find symbol for \"%s\" footprint" ), pcbData->ref );
227  ++errors;
229  }
230  }
231  return errors;
232 }
233 
235 {
236  int errors = 0;
237 
239 
240  std::sort( m_changelist.begin(), m_changelist.end(),
241  []( const CHANGELIST_ITEM& a, const CHANGELIST_ITEM& b )
242  {
243  return SCH_REFERENCE_LIST::sortByTimeStamp( a.first, b.first );
244  } );
245 
246  size_t i = 0;
247  for( auto& item : m_changelist )
248  {
249  // Refs and changelist are both sorted by paths, so we just go over m_refs and
250  // generate errors before we will find m_refs member to which item linked
251  while( i < m_refs.GetCount() && m_refs[i].GetPath() != item.first.GetPath() )
252  {
253  ++errors;
254  wxString msg;
255  msg.Printf( _( "Cannot find footprint for \"%s\" symbol" ), m_refs[i++].GetFullRef() );
257  }
258 
259  ++i;
260  }
261  return errors;
262 }
263 
264 
266 {
267 
268  if( m_settings.processFootprints && aFirst.footprint != aSecond.footprint )
269  return false;
270 
271  if( m_settings.processValues && aFirst.value != aSecond.value )
272  return false;
273  return true;
274 }
275 
276 wxString BACK_ANNOTATE::getTextFromField( const SCH_REFERENCE& aRef, const NumFieldType aField )
277 {
278  return aRef.GetComp()->GetField( aField )->GetText();
279 }
280 
281 
283 {
284 
285  std::sort( m_changelist.begin(), m_changelist.end(),
286  []( CHANGELIST_ITEM& a, CHANGELIST_ITEM& b ) {
287  return a.first.GetComp() > b.first.GetComp();
288  } );
289 
290  // We don't check that if no footprints or values updating
292  return 0;
293  int errors = 0;
294 
295  // We will count how many times every component used in our changelist
296  // Component in this case is SCH_COMPONENT which can be used by more than one symbol
297  int usageCount = 1;
298 
299  for( auto it = m_changelist.begin(); it != m_changelist.end(); ++it )
300  {
301  int compUsage = it->first.GetComp()->GetInstanceReferences().size();
302 
303  if( compUsage == 1 )
304  continue;
305 
306  // If that's not the last reference in list and references share same component
307  if( ( it + 1 ) != m_changelist.end() && it->first.GetComp() == ( it + 1 )->first.GetComp() )
308  {
309  ++usageCount;
310  if( !checkReuseViolation( *it->second, *( it + 1 )->second ) )
311  {
312  // Refs share same component but have different values or footprints
313  ++errors;
314  wxString msg;
315  msg.Printf( _( "\"%s\" and \"%s\" use the same schematic symbol.\n"
316  "They cannot have different footprints or values." ),
317  ( it + 1 )->second->ref, it->second->ref );
319  }
320  }
321  else
322  {
323  /* Next ref uses different component, so we count all components number for current
324  one. We compare that number to stored in the component itself. If that differs, it
325  means that this particular component is reused in some another project. */
326  if( !m_settings.ignoreOtherProjects && compUsage > usageCount )
327  {
328  PCB_MODULE_DATA tmp{ "", getTextFromField( it->first, FOOTPRINT ),
329  getTextFromField( it->first, VALUE ) };
330  if( !checkReuseViolation( tmp, *it->second ) )
331  {
332  wxString msg;
333  msg.Printf( _( "Unable to change \"%s\" footprint or value because associated"
334  " symbol is reused in the another project" ),
335  it->second->ref );
337  ++errors;
338  }
339  }
340  usageCount = 1;
341  }
342  }
343  return errors;
344 }
345 
346 
348 {
349  wxString msg;
350  int leftUnchanged = 0;
351 
352  // Apply changes from change list
353  for( auto& item : m_changelist )
354  {
355  SCH_REFERENCE& ref = item.first;
356  PCB_MODULE_DATA& module = *item.second;
357  wxString oldFootprint = getTextFromField( ref, FOOTPRINT );
358  wxString oldValue = getTextFromField( ref, VALUE );
359  int changesCountBefore = m_changesCount;
360 
361  if( m_settings.processReferences && ref.GetRef() != module.ref )
362  {
363  ++m_changesCount;
364  msg.Printf( _( "Change \"%s\" reference designator to \"%s\"." ), ref.GetFullRef(), module.ref );
365  if( !m_settings.dryRun )
366  ref.GetComp()->SetRef( &ref.GetSheetPath(), module.ref );
368  }
369 
370  if( m_settings.processFootprints && oldFootprint != module.footprint )
371  {
372  ++m_changesCount;
373  msg.Printf( _( "Change %s footprint from \"%s\" to \"%s\"." ), ref.GetFullRef(),
374  getTextFromField( ref, FOOTPRINT ), module.footprint );
375 
376  if( !m_settings.dryRun )
377  ref.GetComp()->GetField( FOOTPRINT )->SetText( module.footprint );
379  }
380 
381  if( m_settings.processValues && oldValue != module.value )
382  {
383  ++m_changesCount;
384  msg.Printf( _( "Change \"%s\" value from \"%s\" to \"\"%s\"." ), ref.GetFullRef(),
385  getTextFromField( ref, VALUE ), module.value );
386  if( !m_settings.dryRun )
387  item.first.GetComp()->GetField( VALUE )->SetText( module.value );
389  }
390 
391  if( changesCountBefore == m_changesCount )
392  ++leftUnchanged;
393  }
394  msg.Printf( _( "%d symbols left unchanged" ), leftUnchanged );
396 }
wxString getTextFromField(const SCH_REFERENCE &aRef, const NumFieldType aField)
Get text from symbol's field ( such as Footprint or Value )
UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to...
Definition: utf8.h:73
SCH_SHEET_LIST.
static bool sortByTimeStamp(const SCH_REFERENCE &item1, const SCH_REFERENCE &item2)
KIWAY_PLAYER is a wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of ...
Definition: kiway_player.h:59
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
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
SCH_SHEET_LIST GetSheets() const
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:99
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:252
SCH_EDIT_FRAME * m_frame
Definition: backannotate.h:121
This file is part of the common library.
int FindRefByPath(const wxString &aPath) const
Searches unit with designated path.
BACK_ANNOTATE(SCH_EDIT_FRAME *aFrame, SETTINGS aSettings)
const SCH_SHEET_PATH & GetSheetPath() const
void Scan(PTREE *aTree, DSNLEXER *aLexer)
Function Scan fills an empty PTREE with information from a KiCad s-expresion stream.
Definition: ptree.cpp:90
SCH_COMPONENT * GetComp() const
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Function ReportTail Places the report at the end of the list, for objects that support report orderin...
Definition: reporter.h:92
int getPcbModulesFromString(const std::string &aPayload)
Parse netlist sent over KiWay epress mail interface and fill m_pcbModules.
PCB_MODULES_MAP m_pcbModules
Definition: backannotate.h:117
Schematic editor (Eeschema) main window.
int getChangeList()
Create changelist
Fetch a netlist from PCB layout.
Definition: mail_type.h:47
Field Name Module PCB, i.e. "16DIP300".
bool BackAnnotateSymbols(const std::string &aNetlist)
Run back annotation algorithm.
void GetMultiUnitComponents(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols=true) const
Function GetMultiUnitComponents adds a SCH_REFERENCE_LIST object to aRefList for each same-reference ...
This file contains miscellaneous commonly used macros and functions.
CHANGELIST m_changelist
Definition: backannotate.h:120
int checkSharedSchematicErrors()
Check for errors connected to reusing schematic in project or between projects.
bool checkReuseViolation(PCB_MODULE_DATA &aFirst, PCB_MODULE_DATA &aSecond)
Check if modules has different data.
wxString GetRef() const
boost::property_tree::ptree PTREE
Definition: ptree.h:54
#define VALUE
void SortByTimeStamp()
Function SortComponentsByTimeStamp sort the flat list by Time Stamp (sheet path + timestamp).
SETTINGS m_settings
Definition: backannotate.h:116
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:116
bool FetchNetlistFromPCB(std::string &aNetlist)
Get netlist from the PCBnew.
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:341
std::pair< SCH_REFERENCE, std::shared_ptr< PCB_MODULE_DATA > > CHANGELIST_ITEM
Definition: backannotate.h:91
void GetComponents(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanComponents=false) const
Function GetComponents adds a SCH_REFERENCE() object to aReferences for each component in the list of...
const PTREE CPTREE
Definition: ptree.h:55
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
SCHEMATIC & Schematic() const
NumFieldType
Enum NumFieldType is the set of all field indices assuming an array like sequence that a SCH_COMPONEN...
virtual REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Function ReportHead Places the report at the beginning of the list for objects that support ordering.
Definition: reporter.h:101
unsigned GetCount() const
Function GetCount.
SCH_FIELD * GetField(int aFieldNdx)
Returns a field in this symbol.
wxString GetFullRef()
Return reference name with unit altogether
VTBL_ENTRY void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=NULL)
Function ExpressMail send aPayload to aDestination from aSource.
Definition: kiway.cpp:427
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void applyChangelist()
Apply changelist to the schematic.
Struct to hold PCBnew module data.
Definition: backannotate.h:77
#define _(s)
Definition: 3d_actions.cpp:33
SCH_REFERENCE_LIST m_refs
Definition: backannotate.h:118
int m_changesCount
To count number of changes applied to the schematic
Definition: backannotate.h:124
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
DSNLEXER implements a lexical analyzer for the SPECCTRA DSN file format.
Definition: dsnlexer.h:79
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:126
settings struct to set up back annotation
Definition: backannotate.h:64
SCH_REFERENCE is used as a helper to define a component's reference designator in a schematic.
int checkForUnusedSymbols()
Check if some symbols are not represented in PCB modules and vice versa.
SCH_MULTI_UNIT_REFERENCE_MAP m_multiUnitsRefs
Definition: backannotate.h:119