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.

398 lines
12 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr>
  5. * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <footprint_info_impl.h>
  22. #include <dialogs/html_message_box.h>
  23. #include <footprint.h>
  24. #include <footprint_info.h>
  25. #include <fp_lib_table.h>
  26. #include <kiway.h>
  27. #include <locale_io.h>
  28. #include <lib_id.h>
  29. #include <progress_reporter.h>
  30. #include <string_utils.h>
  31. #include <core/thread_pool.h>
  32. #include <wildcards_and_files_ext.h>
  33. #include <kiplatform/io.h>
  34. #include <wx/textfile.h>
  35. #include <wx/txtstrm.h>
  36. #include <wx/wfstream.h>
  37. void FOOTPRINT_INFO_IMPL::load()
  38. {
  39. FP_LIB_TABLE* fptable = m_owner->GetTable();
  40. wxASSERT( fptable );
  41. const FOOTPRINT* footprint = fptable->GetEnumeratedFootprint( m_nickname, m_fpname );
  42. if( footprint == nullptr ) // Should happen only with malformed/broken libraries
  43. {
  44. m_pad_count = 0;
  45. m_unique_pad_count = 0;
  46. }
  47. else
  48. {
  49. m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
  50. m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
  51. m_keywords = footprint->GetKeywords();
  52. m_doc = footprint->GetLibDescription();
  53. }
  54. m_loaded = true;
  55. }
  56. void FOOTPRINT_LIST_IMPL::Clear()
  57. {
  58. m_list.clear();
  59. m_list_timestamp = 0;
  60. }
  61. bool FOOTPRINT_LIST_IMPL::CatchErrors( const std::function<void()>& aFunc )
  62. {
  63. try
  64. {
  65. aFunc();
  66. }
  67. catch( const IO_ERROR& ioe )
  68. {
  69. m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
  70. return false;
  71. }
  72. catch( const std::exception& se )
  73. {
  74. // This is a round about way to do this, but who knows what THROW_IO_ERROR()
  75. // may be tricked out to do someday, keep it in the game.
  76. try
  77. {
  78. THROW_IO_ERROR( se.what() );
  79. }
  80. catch( const IO_ERROR& ioe )
  81. {
  82. m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
  83. }
  84. return false;
  85. }
  86. return true;
  87. }
  88. bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname,
  89. PROGRESS_REPORTER* aProgressReporter )
  90. {
  91. long long int generatedTimestamp = 0;
  92. if( !CatchErrors( [&]()
  93. {
  94. generatedTimestamp = aTable->GenerateTimestamp( aNickname );
  95. } ) )
  96. {
  97. return false;
  98. }
  99. if( generatedTimestamp == m_list_timestamp )
  100. return true;
  101. // Disable KIID generation: not needed for library parts; sometimes very slow
  102. KIID_NIL_SET_RESET reset_kiid;
  103. m_progress_reporter = aProgressReporter;
  104. if( m_progress_reporter )
  105. {
  106. m_progress_reporter->SetMaxProgress( m_queue_in.size() );
  107. m_progress_reporter->Report( _( "Fetching footprint libraries..." ) );
  108. }
  109. m_cancelled = false;
  110. m_lib_table = aTable;
  111. // Clear data before reading files
  112. m_errors.clear();
  113. m_list.clear();
  114. m_queue_in.clear();
  115. m_queue_out.clear();
  116. if( aNickname )
  117. {
  118. m_queue_in.push( *aNickname );
  119. }
  120. else
  121. {
  122. for( const wxString& nickname : aTable->GetLogicalLibs() )
  123. m_queue_in.push( nickname );
  124. }
  125. loadLibs();
  126. if( !m_cancelled )
  127. {
  128. if( m_progress_reporter )
  129. {
  130. m_progress_reporter->SetMaxProgress( m_queue_out.size() );
  131. m_progress_reporter->AdvancePhase();
  132. m_progress_reporter->Report( _( "Loading footprints..." ) );
  133. }
  134. loadFootprints();
  135. if( m_progress_reporter )
  136. m_progress_reporter->AdvancePhase();
  137. }
  138. if( m_cancelled )
  139. m_list_timestamp = 0; // God knows what we got before we were canceled
  140. else
  141. m_list_timestamp = generatedTimestamp;
  142. return m_errors.empty();
  143. }
  144. void FOOTPRINT_LIST_IMPL::loadLibs()
  145. {
  146. thread_pool& tp = GetKiCadThreadPool();
  147. size_t num_returns = m_queue_in.size();
  148. std::vector<std::future<size_t>> returns( num_returns );
  149. auto loader_job =
  150. [this]() -> size_t
  151. {
  152. wxString nickname;
  153. size_t retval = 0;
  154. if( !m_cancelled && m_queue_in.pop( nickname ) )
  155. {
  156. if( CatchErrors( [this, &nickname]()
  157. {
  158. m_lib_table->PrefetchLib( nickname );
  159. m_queue_out.push( nickname );
  160. } ) && m_progress_reporter )
  161. {
  162. m_progress_reporter->AdvanceProgress();
  163. }
  164. ++retval;
  165. }
  166. return retval;
  167. };
  168. for( size_t ii = 0; ii < num_returns; ++ii )
  169. returns[ii] = tp.submit( loader_job );
  170. for( const std::future<size_t>& ret : returns )
  171. {
  172. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  173. while( status != std::future_status::ready )
  174. {
  175. if( m_progress_reporter && !m_progress_reporter->KeepRefreshing() )
  176. m_cancelled = true;
  177. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  178. }
  179. }
  180. }
  181. void FOOTPRINT_LIST_IMPL::loadFootprints()
  182. {
  183. LOCALE_IO toggle_locale;
  184. // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
  185. // GLOBAL. It is only thread safe to construct the LOCALE_IO before the threads are created,
  186. // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
  187. // from this will cause nasal demons.
  188. //
  189. // TODO: blast LOCALE_IO into the sun
  190. SYNC_QUEUE<std::unique_ptr<FOOTPRINT_INFO>> queue_parsed;
  191. thread_pool& tp = GetKiCadThreadPool();
  192. size_t num_elements = m_queue_out.size();
  193. std::vector<std::future<size_t>> returns( num_elements );
  194. auto fp_thread =
  195. [ this, &queue_parsed ]() -> size_t
  196. {
  197. wxString nickname;
  198. if( m_cancelled || !m_queue_out.pop( nickname ) )
  199. return 0;
  200. wxArrayString fpnames;
  201. CatchErrors(
  202. [&]()
  203. {
  204. m_lib_table->FootprintEnumerate( fpnames, nickname, false );
  205. } );
  206. for( wxString fpname : fpnames )
  207. {
  208. CatchErrors(
  209. [&]()
  210. {
  211. auto* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
  212. queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
  213. } );
  214. if( m_cancelled )
  215. return 0;
  216. }
  217. if( m_progress_reporter )
  218. m_progress_reporter->AdvanceProgress();
  219. return 1;
  220. };
  221. for( size_t ii = 0; ii < num_elements; ++ii )
  222. returns[ii] = tp.submit( fp_thread );
  223. for( const std::future<size_t>& ret : returns )
  224. {
  225. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  226. while( status != std::future_status::ready )
  227. {
  228. if( m_progress_reporter )
  229. m_progress_reporter->KeepRefreshing();
  230. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  231. }
  232. }
  233. std::unique_ptr<FOOTPRINT_INFO> fpi;
  234. while( queue_parsed.pop( fpi ) )
  235. m_list.push_back( std::move( fpi ) );
  236. std::sort( m_list.begin(), m_list.end(),
  237. []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
  238. std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool
  239. {
  240. return *lhs < *rhs;
  241. } );
  242. }
  243. FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() :
  244. m_list_timestamp( 0 ),
  245. m_progress_reporter( nullptr ),
  246. m_cancelled( false )
  247. {
  248. }
  249. void FOOTPRINT_LIST_IMPL::WriteCacheToFile( const wxString& aFilePath )
  250. {
  251. wxFileName tmpFileName = wxFileName::CreateTempFileName( aFilePath );
  252. wxFFileOutputStream outStream( tmpFileName.GetFullPath() );
  253. wxTextOutputStream txtStream( outStream );
  254. if( !outStream.IsOk() )
  255. {
  256. return;
  257. }
  258. txtStream << wxString::Format( wxT( "%lld" ), m_list_timestamp ) << endl;
  259. for( std::unique_ptr<FOOTPRINT_INFO>& fpinfo : m_list )
  260. {
  261. txtStream << fpinfo->GetLibNickname() << endl;
  262. txtStream << fpinfo->GetName() << endl;
  263. txtStream << EscapeString( fpinfo->GetDesc(), CTX_LINE ) << endl;
  264. txtStream << EscapeString( fpinfo->GetKeywords(), CTX_LINE ) << endl;
  265. txtStream << wxString::Format( wxT( "%d" ), fpinfo->GetOrderNum() ) << endl;
  266. txtStream << wxString::Format( wxT( "%u" ), fpinfo->GetPadCount() ) << endl;
  267. txtStream << wxString::Format( wxT( "%u" ), fpinfo->GetUniquePadCount() ) << endl;
  268. }
  269. txtStream.Flush();
  270. outStream.Close();
  271. // Preserve the permissions of the current file
  272. KIPLATFORM::IO::DuplicatePermissions( aFilePath, tmpFileName.GetFullPath() );
  273. if( !wxRenameFile( tmpFileName.GetFullPath(), aFilePath, true ) )
  274. {
  275. // cleanup in case rename failed
  276. // its also not the end of the world since this is just a cache file
  277. wxRemoveFile( tmpFileName.GetFullPath() );
  278. }
  279. }
  280. void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( const wxString& aFilePath )
  281. {
  282. wxTextFile cacheFile( aFilePath );
  283. m_list_timestamp = 0;
  284. m_list.clear();
  285. try
  286. {
  287. if( cacheFile.Exists() && cacheFile.Open() )
  288. {
  289. cacheFile.GetFirstLine().ToLongLong( &m_list_timestamp );
  290. while( cacheFile.GetCurrentLine() + 6 < cacheFile.GetLineCount() )
  291. {
  292. wxString libNickname = cacheFile.GetNextLine();
  293. wxString name = cacheFile.GetNextLine();
  294. wxString desc = UnescapeString( cacheFile.GetNextLine() );
  295. wxString keywords = UnescapeString( cacheFile.GetNextLine() );
  296. int orderNum = wxAtoi( cacheFile.GetNextLine() );
  297. unsigned int padCount = (unsigned) wxAtoi( cacheFile.GetNextLine() );
  298. unsigned int uniquePadCount = (unsigned) wxAtoi( cacheFile.GetNextLine() );
  299. FOOTPRINT_INFO_IMPL* fpinfo = new FOOTPRINT_INFO_IMPL( libNickname, name, desc,
  300. keywords, orderNum,
  301. padCount, uniquePadCount );
  302. m_list.emplace_back( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
  303. }
  304. }
  305. }
  306. catch( ... )
  307. {
  308. // whatever went wrong, invalidate the cache
  309. m_list_timestamp = 0;
  310. }
  311. // Sanity check: an empty list is very unlikely to be correct.
  312. if( m_list.size() == 0 )
  313. m_list_timestamp = 0;
  314. if( cacheFile.IsOpened() )
  315. cacheFile.Close();
  316. }