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.

471 lines
14 KiB

  1. /**
  2. * @file pcbnew/dialogs/dialog_netlist.cpp
  3. */
  4. /*
  5. * This program source code file is part of KiCad, a free EDA CAD application.
  6. *
  7. * Copyright (C) 1992-2013 KiCad Developers, see change_log.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <fctsys.h>
  27. #include <appl_wxstruct.h>
  28. #include <confirm.h>
  29. #include <dialog_helpers.h>
  30. #include <html_messagebox.h>
  31. #include <base_units.h>
  32. #include <wxPcbStruct.h>
  33. #include <pcbcommon.h>
  34. #include <netlist_reader.h>
  35. #include <reporter.h>
  36. #include <pcbnew_config.h>
  37. #include <class_board_design_settings.h>
  38. #include <class_board.h>
  39. #include <class_module.h>
  40. #include <wildcards_and_files_ext.h>
  41. #include <dialog_netlist.h>
  42. #define NETLIST_SILENTMODE_KEY wxT("SilentMode")
  43. void PCB_EDIT_FRAME::InstallNetlistFrame( wxDC* DC )
  44. {
  45. /* Setup the netlist file name to the last netlist file read,
  46. * or the board file name if the last filename is empty or last file not existing.
  47. */
  48. wxFileName fn = GetLastNetListRead();
  49. wxString lastNetlistName = GetLastNetListRead();
  50. if( !fn.FileExists() )
  51. {
  52. fn = GetBoard()->GetFileName();
  53. fn.SetExt( NetlistFileExtension );
  54. lastNetlistName = fn.GetFullPath();
  55. }
  56. DIALOG_NETLIST dlg( this, DC, lastNetlistName );
  57. dlg.ShowModal();
  58. // Save project settings if needed.
  59. // Project settings are saved in the corresponding <board name>.pro file
  60. bool configChanged = lastNetlistName != GetLastNetListRead();
  61. if( dlg.UseCmpFileForFpNames() != GetUseCmpFileForFpNames() )
  62. {
  63. SetUseCmpFileForFpNames( dlg.UseCmpFileForFpNames() );
  64. configChanged = true;
  65. }
  66. if( configChanged
  67. && !GetBoard()->GetFileName().IsEmpty()
  68. && IsOK( NULL, _( "The project configuration has changed. Do you want to save it?" ) ) )
  69. {
  70. wxFileName fn = GetBoard()->GetFileName();
  71. fn.SetExt( ProjectFileExtension );
  72. wxGetApp().WriteProjectConfig( fn.GetFullPath(), GROUP, GetProjectFileParameters() );
  73. }
  74. }
  75. DIALOG_NETLIST::DIALOG_NETLIST( PCB_EDIT_FRAME* aParent, wxDC * aDC,
  76. const wxString & aNetlistFullFilename )
  77. : DIALOG_NETLIST_FBP( aParent )
  78. {
  79. m_parent = aParent;
  80. m_dc = aDC;
  81. m_config = wxGetApp().GetSettings();
  82. m_silentMode = m_config->Read( NETLIST_SILENTMODE_KEY, 0l );
  83. m_NetlistFilenameCtrl->SetValue( aNetlistFullFilename );
  84. m_cmpNameSourceOpt->SetSelection( m_parent->GetUseCmpFileForFpNames() ? 1 : 0 );
  85. m_checkBoxSilentMode->SetValue( m_silentMode );
  86. GetSizer()->SetSizeHints( this );
  87. }
  88. DIALOG_NETLIST::~DIALOG_NETLIST()
  89. {
  90. m_config->Write( NETLIST_SILENTMODE_KEY, (long) m_silentMode );
  91. }
  92. void DIALOG_NETLIST::OnOpenNetlistClick( wxCommandEvent& event )
  93. {
  94. wxString lastPath = wxFileName::GetCwd();
  95. wxString lastNetlistRead = m_parent->GetLastNetListRead();
  96. if( !lastNetlistRead.IsEmpty() && !wxFileName::FileExists( lastNetlistRead ) )
  97. {
  98. lastNetlistRead = wxEmptyString;
  99. }
  100. else
  101. {
  102. wxFileName fn = lastNetlistRead;
  103. lastPath = fn.GetPath();
  104. lastNetlistRead = fn.GetFullName();
  105. }
  106. wxLogDebug( wxT( "Last net list read path <%s>, file name <%s>." ),
  107. GetChars( lastPath ), GetChars( lastNetlistRead ) );
  108. wxFileDialog FilesDialog( this, _( "Select Netlist" ), lastPath, lastNetlistRead,
  109. NetlistFileWildcard, wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST );
  110. if( FilesDialog.ShowModal() != wxID_OK )
  111. return;
  112. m_NetlistFilenameCtrl->SetValue( FilesDialog.GetPath() );
  113. }
  114. void DIALOG_NETLIST::OnReadNetlistFileClick( wxCommandEvent& event )
  115. {
  116. wxString msg;
  117. wxString netlistFileName = m_NetlistFilenameCtrl->GetValue();
  118. wxString cmpFileName;
  119. if( UseCmpFileForFpNames() )
  120. {
  121. wxFileName fn = m_NetlistFilenameCtrl->GetValue();
  122. fn.SetExt( ComponentFileExtension );
  123. cmpFileName = fn.GetFullPath();
  124. }
  125. // Give the user a chance to bail out when making changes from a netlist.
  126. if( !m_checkDryRun->GetValue() && !m_silentMode
  127. && !m_parent->GetBoard()->IsEmpty()
  128. && !IsOK( NULL, _( "The changes made by reading the netlist cannot be undone. Are you "
  129. "sure you want to read the netlist?" ) ) )
  130. return;
  131. wxBusyCursor busy();
  132. m_MessageWindow->Clear();
  133. msg.Printf( _( "Reading netlist file \"%s\".\n" ), GetChars( netlistFileName ) );
  134. m_MessageWindow->AppendText( msg );
  135. if( !cmpFileName.IsEmpty() )
  136. {
  137. msg.Printf( _( "Using component footprint link file \"%s\".\n" ), GetChars( cmpFileName ) );
  138. m_MessageWindow->AppendText( msg );
  139. }
  140. if( m_Select_By_Timestamp->GetSelection() == 1 )
  141. {
  142. msg.Printf( _( "Using time stamps to select footprints in file \"%s\".\n" ),
  143. GetChars( cmpFileName ) );
  144. m_MessageWindow->AppendText( msg );
  145. }
  146. WX_TEXT_CTRL_REPORTER reporter( m_MessageWindow );
  147. m_parent->ReadPcbNetlist( netlistFileName, cmpFileName, &reporter,
  148. m_ChangeExistingFootprintCtrl->GetSelection() == 1,
  149. m_DeleteBadTracks->GetSelection() == 1,
  150. m_RemoveExtraFootprintsCtrl->GetSelection() == 1,
  151. m_Select_By_Timestamp->GetSelection() == 1,
  152. m_checkDryRun->GetValue() );
  153. }
  154. void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event )
  155. {
  156. if( m_parent->GetBoard()->m_Modules == NULL )
  157. {
  158. DisplayInfoMessage( this, _( "No modules" ) );
  159. return;
  160. }
  161. // Lists of duplicates, missing references and not in netlist footprints:
  162. std::vector <MODULE*> duplicate;
  163. wxArrayString missing;
  164. std::vector <MODULE*> notInNetlist;
  165. wxString netlistFilename = m_NetlistFilenameCtrl->GetValue();
  166. wxString cmpFilename;
  167. if( UseCmpFileForFpNames() )
  168. {
  169. wxFileName fn = m_NetlistFilenameCtrl->GetValue();
  170. fn.SetExt( ComponentFileExtension );
  171. cmpFilename = fn.GetFullPath();
  172. }
  173. if( !verifyFootprints( netlistFilename, cmpFilename, duplicate, missing, notInNetlist ) )
  174. return;
  175. #define ERR_CNT_MAX 100 // Max number of errors to output in dialog
  176. // to avoid a too long message list
  177. wxString list; // The messages to display
  178. m_parent->SetLastNetListRead( netlistFilename );
  179. int err_cnt = 0;
  180. // Search for duplicate footprints.
  181. if( duplicate.size() == 0 )
  182. list << wxT("<p><b>") << _( "No duplicate." ) << wxT("</b></p>");
  183. else
  184. {
  185. list << wxT("<p><b>") << _( "Duplicates:" ) << wxT("</b></p>");
  186. for( unsigned ii = 0; ii < duplicate.size(); ii++ )
  187. {
  188. MODULE* module = duplicate[ii];
  189. if( module->GetReference().IsEmpty() )
  190. list << wxT("<br>") << wxT("[noref)");
  191. else
  192. list << wxT("<br>") << module->GetReference();
  193. list << wxT(" (<i>") << module->GetValue() << wxT("</i>)");
  194. list << wxT(" @ ");
  195. list << CoordinateToString( module->GetPosition().x ),
  196. list << wxT(", ") << CoordinateToString( module->GetPosition().y ),
  197. err_cnt++;
  198. if( ERR_CNT_MAX < err_cnt )
  199. break;
  200. }
  201. }
  202. // Search for missing modules on board.
  203. if( missing.size() == 0 )
  204. list << wxT("<p><b>") << _( "No missing modules." ) << wxT("</b></p>");
  205. else
  206. {
  207. list << wxT("<p><b>") << _( "Missing:" ) << wxT("</b></p>");
  208. for( unsigned ii = 0; ii < missing.size(); ii += 2 )
  209. {
  210. list << wxT("<br>") << missing[ii];
  211. list << wxT(" (<i>") << missing[ii+1] << wxT("</i>)");
  212. err_cnt++;
  213. if( ERR_CNT_MAX < err_cnt )
  214. break;
  215. }
  216. }
  217. // Search for modules found on board but not in net list.
  218. if( notInNetlist.size() == 0 )
  219. list << wxT( "<p><b>" ) << _( "No extra modules." ) << wxT( "</b></p>" );
  220. else
  221. {
  222. list << wxT( "<p><b>" ) << _( "Not in Netlist:" ) << wxT( "</b></p>" );
  223. for( unsigned ii = 0; ii < notInNetlist.size(); ii++ )
  224. {
  225. MODULE* module = notInNetlist[ii];
  226. if( module->GetReference().IsEmpty() )
  227. list << wxT( "<br>" ) << wxT( "[noref)" );
  228. else
  229. list << wxT( "<br>" ) << module->GetReference() ;
  230. list << wxT( " (<i>" ) << module->GetValue() << wxT( "</i>)" );
  231. list << wxT( " @ " );
  232. list << CoordinateToString( module->GetPosition().x ),
  233. list << wxT( ", " ) << CoordinateToString( module->GetPosition().y ),
  234. err_cnt++;
  235. if( ERR_CNT_MAX < err_cnt )
  236. break;
  237. }
  238. }
  239. if( ERR_CNT_MAX < err_cnt )
  240. {
  241. list << wxT( "<p><b>" )
  242. << _( "Too many errors: some are skipped" )
  243. << wxT( "</b></p>" );
  244. }
  245. HTML_MESSAGE_BOX dlg( this, _( "Check Modules" ) );
  246. dlg.AddHTML_Text( list );
  247. dlg.ShowModal();
  248. }
  249. /*!
  250. * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_COMPILE_RATSNEST
  251. */
  252. void DIALOG_NETLIST::OnCompileRatsnestClick( wxCommandEvent& event )
  253. {
  254. m_parent->Compile_Ratsnest( m_dc, true );
  255. }
  256. /*!
  257. * wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_CANCEL
  258. */
  259. void DIALOG_NETLIST::OnCancelClick( wxCommandEvent& event )
  260. {
  261. EndModal( wxID_CANCEL );
  262. }
  263. void DIALOG_NETLIST::OnSaveMessagesToFile( wxCommandEvent& aEvent )
  264. {
  265. wxFileName fn;
  266. if( !m_parent->GetLastNetListRead().IsEmpty() )
  267. {
  268. fn = m_parent->GetLastNetListRead();
  269. fn.SetExt( wxT( "txt" ) );
  270. }
  271. else
  272. {
  273. fn.SetPath( wxFileName::GetCwd() );
  274. }
  275. wxFileDialog dlg( this, _( "Save contents of message window" ), fn.GetPath(), fn.GetName(),
  276. TextWildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  277. if( dlg.ShowModal() != wxID_OK )
  278. return;
  279. fn = dlg.GetPath();
  280. if( fn.GetExt().IsEmpty() )
  281. fn.SetExt( wxT( "txt" ) );
  282. wxFile f( fn.GetFullPath(), wxFile::write );
  283. if( !f.IsOpened() )
  284. {
  285. wxString msg;
  286. msg.Printf( _( "Cannot write message contents to file \"%s\"." ),
  287. GetChars( fn.GetFullPath() ) );
  288. wxMessageBox( msg, _( "File Write Error" ), wxOK | wxICON_ERROR, this );
  289. return;
  290. }
  291. f.Write( m_MessageWindow->GetValue() );
  292. }
  293. void DIALOG_NETLIST::OnUpdateUISaveMessagesToFile( wxUpdateUIEvent& aEvent )
  294. {
  295. aEvent.Enable( !m_MessageWindow->IsEmpty() );
  296. }
  297. void DIALOG_NETLIST::OnUpdateUIValidNetlistFile( wxUpdateUIEvent& aEvent )
  298. {
  299. aEvent.Enable( !m_NetlistFilenameCtrl->GetValue().IsEmpty() );
  300. }
  301. bool DIALOG_NETLIST::verifyFootprints( const wxString& aNetlistFilename,
  302. const wxString & aCmpFilename,
  303. std::vector< MODULE* >& aDuplicates,
  304. wxArrayString& aMissing,
  305. std::vector< MODULE* >& aNotInNetlist )
  306. {
  307. wxString msg;
  308. MODULE* module;
  309. MODULE* nextModule;
  310. NETLIST netlist;
  311. wxBusyCursor dummy; // Shows an hourglass while calculating.
  312. NETLIST_READER* netlistReader;
  313. COMPONENT* component;
  314. try
  315. {
  316. netlistReader = NETLIST_READER::GetNetlistReader( &netlist, aNetlistFilename,
  317. aCmpFilename );
  318. if( netlistReader == NULL )
  319. {
  320. msg.Printf( _( "Cannot open netlist file \"%s\"." ), GetChars( aNetlistFilename ) );
  321. wxMessageBox( msg, _( "Netlist Load Error." ), wxOK | wxICON_ERROR );
  322. return false;
  323. }
  324. std::auto_ptr< NETLIST_READER > nlr( netlistReader );
  325. netlistReader->LoadNetlist();
  326. }
  327. catch( IO_ERROR& ioe )
  328. {
  329. msg.Printf( _( "Error loading netlist file:\n%s" ), ioe.errorText.GetData() );
  330. wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR );
  331. return false;
  332. }
  333. #if defined( DEBUG )
  334. m_MessageWindow->Clear();
  335. WX_TEXT_CTRL_REPORTER rpt( m_MessageWindow );
  336. netlist.Show( 0, rpt );
  337. #endif
  338. BOARD* pcb = m_parent->GetBoard();
  339. // Search for duplicate footprints.
  340. module = pcb->m_Modules;
  341. for( ; module != NULL; module = module->Next() )
  342. {
  343. nextModule = module->Next();
  344. for( ; nextModule != NULL; nextModule = nextModule->Next() )
  345. {
  346. if( module->GetReference().CmpNoCase( nextModule->GetReference() ) == 0 )
  347. {
  348. aDuplicates.push_back( module );
  349. break;
  350. }
  351. }
  352. }
  353. // Search for component footprints in the netlist but not on the board.
  354. for( unsigned ii = 0; ii < netlist.GetCount(); ii++ )
  355. {
  356. component = netlist.GetComponent( ii );
  357. module = pcb->FindModuleByReference( component->GetReference() );
  358. if( module == NULL )
  359. {
  360. aMissing.Add( component->GetReference() );
  361. aMissing.Add( component->GetValue() );
  362. }
  363. }
  364. // Search for component footprints found on board but not in netlist.
  365. module = pcb->m_Modules;
  366. for( ; module != NULL; module = module->Next() )
  367. {
  368. component = netlist.GetComponentByReference( module->GetReference() );
  369. if( component == NULL )
  370. aNotInNetlist.push_back( module );
  371. }
  372. return true;
  373. }