You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
9.8 KiB

6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2022 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. * @file auto_associate.cpp
  25. */
  26. // This file handle automatic selection of footprints, from .equ files which give
  27. // a footprint LIB_ID associated to a component value.
  28. // These associations have this form:
  29. // 'FT232BL' 'QFP:LQFP-32_7x7mm_Pitch0.8mm'
  30. #include <kiface_base.h>
  31. #include <string_utils.h>
  32. #include <macros.h>
  33. #include <auto_associate.h>
  34. #include <cvpcb_association.h>
  35. #include <cvpcb_mainframe.h>
  36. #include <listboxes.h>
  37. #include <project/project_file.h>
  38. #include <wx/msgdlg.h>
  39. #define QUOTE '\''
  40. /**
  41. * Read the string between quotes.
  42. *
  43. * @return a the quoted string.
  44. */
  45. wxString GetQuotedText( wxString& text )
  46. {
  47. int i = text.Find( QUOTE );
  48. if( wxNOT_FOUND == i )
  49. return wxT( "" );
  50. wxString shrt = text.Mid( i + 1 );
  51. i = shrt.Find( QUOTE );
  52. if( wxNOT_FOUND == i )
  53. return wxT( "" );
  54. text = shrt.Mid( i + 1 );
  55. return shrt.Mid( 0, i );
  56. }
  57. // A sort compare function, used to sort a FOOTPRINT_EQUIVALENCE_LIST by cmp values
  58. // (m_ComponentValue member)
  59. bool sortListbyCmpValue( const FOOTPRINT_EQUIVALENCE& ref, const FOOTPRINT_EQUIVALENCE& test )
  60. {
  61. return ref.m_ComponentValue.Cmp( test.m_ComponentValue ) >= 0;
  62. }
  63. int CVPCB_MAINFRAME::buildEquivalenceList( FOOTPRINT_EQUIVALENCE_LIST& aList,
  64. wxString* aErrorMessages )
  65. {
  66. char line[1024];
  67. int error_count = 0;
  68. FILE* file;
  69. wxFileName fn;
  70. wxString tmp, error_msg;
  71. SEARCH_STACK& search = Kiface().KifaceSearch();
  72. PROJECT_FILE& project = Prj().GetProjectFile();
  73. // Find equivalences in all available files, and populates the
  74. // equiv_List with all equivalences found in .equ files
  75. for( const wxString& equfile : project.m_EquivalenceFiles )
  76. {
  77. fn = wxExpandEnvVars( equfile );
  78. if( fn.IsAbsolute() || fn.FileExists() )
  79. tmp = fn.GetFullPath();
  80. else
  81. tmp = search.FindValidPath( fn.GetFullPath() );
  82. if( !tmp )
  83. {
  84. error_count++;
  85. if( aErrorMessages )
  86. {
  87. error_msg.Printf( _( "Equivalence file '%s' could not be found." ),
  88. fn.GetFullName() );
  89. if( ! aErrorMessages->IsEmpty() )
  90. *aErrorMessages << wxT("\n\n");
  91. *aErrorMessages += error_msg;
  92. }
  93. continue;
  94. }
  95. file = wxFopen( tmp, wxT( "rt" ) );
  96. if( file == nullptr )
  97. {
  98. error_count++;
  99. if( aErrorMessages )
  100. {
  101. error_msg.Printf( _( "Error opening equivalence file '%s'." ), tmp );
  102. if( ! aErrorMessages->IsEmpty() )
  103. *aErrorMessages << wxT("\n\n");
  104. *aErrorMessages += error_msg;
  105. }
  106. continue;
  107. }
  108. while( GetLine( file, line, nullptr, sizeof( line ) ) != nullptr )
  109. {
  110. if( *line == 0 )
  111. continue;
  112. wxString wtext = From_UTF8( line );
  113. wxString value = GetQuotedText( wtext );
  114. if( value.IsEmpty() )
  115. continue;
  116. wxString footprint = GetQuotedText( wtext );
  117. if( footprint.IsEmpty() )
  118. continue;
  119. value.Replace( wxT( " " ), wxT( "_" ) );
  120. FOOTPRINT_EQUIVALENCE* equivItem = new FOOTPRINT_EQUIVALENCE();
  121. equivItem->m_ComponentValue = value;
  122. equivItem->m_FootprintFPID = footprint;
  123. aList.push_back( equivItem );
  124. }
  125. fclose( file );
  126. }
  127. return error_count;
  128. }
  129. void CVPCB_MAINFRAME::AutomaticFootprintMatching()
  130. {
  131. FOOTPRINT_EQUIVALENCE_LIST equivList;
  132. wxString msg;
  133. wxString error_msg;
  134. if( m_netlist.IsEmpty() )
  135. return;
  136. if( buildEquivalenceList( equivList, &error_msg ) )
  137. wxMessageBox( error_msg, _( "Equivalence File Load Error" ), wxOK | wxICON_WARNING, this );
  138. // Sort the association list by symbol value. When sorted, finding duplicate definitions
  139. // (i.e. 2 or more items having the same symbol value) is easier.
  140. std::sort( equivList.begin(), equivList.end(), sortListbyCmpValue );
  141. // Display the number of footprint/symbol equivalences.
  142. msg.Printf( _( "%lu footprint/symbol equivalences found." ), (unsigned long)equivList.size() );
  143. SetStatusText( msg, 0 );
  144. // Now, associate each free component with a footprint
  145. m_skipComponentSelect = true;
  146. error_msg.Empty();
  147. bool firstAssoc = true;
  148. for( unsigned kk = 0; kk < m_netlist.GetCount(); kk++ )
  149. {
  150. COMPONENT* component = m_netlist.GetComponent( kk );
  151. bool found = false;
  152. if( !component->GetFPID().empty() ) // the component has already a footprint
  153. continue;
  154. // Here a first attempt is made. We can have multiple equivItem of the same value.
  155. // When happens, using the footprint filter of components can remove the ambiguity by
  156. // filtering equivItem so one can use multiple equivList (for polar and non-polar caps
  157. // for example)
  158. wxString fpid_candidate;
  159. for( unsigned idx = 0; idx < equivList.size(); idx++ )
  160. {
  161. FOOTPRINT_EQUIVALENCE& equivItem = equivList[idx];
  162. if( equivItem.m_ComponentValue.CmpNoCase( component->GetValue() ) != 0 )
  163. continue;
  164. const FOOTPRINT_INFO *fp = m_FootprintsList->GetFootprintInfo( equivItem.m_FootprintFPID );
  165. bool equ_is_unique = true;
  166. unsigned next = idx+1;
  167. int previous = idx-1;
  168. if( next < equivList.size()
  169. && equivItem.m_ComponentValue == equivList[next].m_ComponentValue )
  170. {
  171. equ_is_unique = false;
  172. }
  173. if( previous >= 0
  174. && equivItem.m_ComponentValue == equivList[previous].m_ComponentValue )
  175. {
  176. equ_is_unique = false;
  177. }
  178. // If the equivalence is unique, no ambiguity: use the association
  179. if( fp && equ_is_unique )
  180. {
  181. AssociateFootprint( CVPCB_ASSOCIATION( kk, equivItem.m_FootprintFPID ),
  182. firstAssoc );
  183. firstAssoc = false;
  184. found = true;
  185. break;
  186. }
  187. // Store the first candidate found in list, when equivalence is not unique
  188. // We use it later.
  189. if( fp && fpid_candidate.IsEmpty() )
  190. fpid_candidate = equivItem.m_FootprintFPID;
  191. // The equivalence is not unique: use the footprint filter to try to remove
  192. // ambiguity
  193. // if the footprint filter does not remove ambiguity, we will use fpid_candidate
  194. if( fp )
  195. {
  196. size_t filtercount = component->GetFootprintFilters().GetCount();
  197. found = ( 0 == filtercount ); // if no entries, do not filter
  198. for( size_t jj = 0; jj < filtercount && !found; jj++ )
  199. found = fp->GetFootprintName().Matches( component->GetFootprintFilters()[jj] );
  200. }
  201. else
  202. {
  203. msg.Printf( _( "Component %s: footprint %s not found in any of the project "
  204. "footprint libraries." ),
  205. component->GetReference(), equivItem.m_FootprintFPID );
  206. if( ! error_msg.IsEmpty() )
  207. error_msg << wxT("\n\n");
  208. error_msg += msg;
  209. }
  210. if( found )
  211. {
  212. AssociateFootprint( CVPCB_ASSOCIATION( kk, equivItem.m_FootprintFPID ),
  213. firstAssoc );
  214. firstAssoc = false;
  215. break;
  216. }
  217. }
  218. if( found )
  219. {
  220. continue;
  221. }
  222. else if( !fpid_candidate.IsEmpty() )
  223. {
  224. AssociateFootprint( CVPCB_ASSOCIATION( kk, fpid_candidate ), firstAssoc );
  225. firstAssoc = false;
  226. continue;
  227. }
  228. // obviously the last chance: there's only one filter matching one footprint
  229. if( 1 == component->GetFootprintFilters().GetCount() )
  230. {
  231. // we do not need to analyze wildcards: single footprint do not
  232. // contain them and if there are wildcards it just will not match any
  233. if( m_FootprintsList->GetFootprintInfo( component->GetFootprintFilters()[0] ) )
  234. {
  235. AssociateFootprint( CVPCB_ASSOCIATION( kk, component->GetFootprintFilters()[0] ),
  236. firstAssoc );
  237. firstAssoc = false;
  238. }
  239. }
  240. }
  241. if( !error_msg.IsEmpty() )
  242. wxMessageBox( error_msg, _( "CvPcb Warning" ), wxOK | wxICON_WARNING, this );
  243. m_skipComponentSelect = false;
  244. m_symbolsListBox->Refresh();
  245. }