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.

296 lines
8.4 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <design_block_info_impl.h>
  20. #include <design_block.h>
  21. #include <design_block_info.h>
  22. #include <design_block_lib_table.h>
  23. #include <kiway.h>
  24. #include <locale_io.h>
  25. #include <lib_id.h>
  26. #include <progress_reporter.h>
  27. #include <string_utils.h>
  28. #include <core/thread_pool.h>
  29. #include <wildcards_and_files_ext.h>
  30. #include <kiplatform/io.h>
  31. #include <wx/textfile.h>
  32. #include <wx/txtstrm.h>
  33. #include <wx/wfstream.h>
  34. void DESIGN_BLOCK_INFO_IMPL::load()
  35. {
  36. DESIGN_BLOCK_LIB_TABLE* dbtable = m_owner->GetTable();
  37. wxASSERT( dbtable );
  38. const DESIGN_BLOCK* design_block = dbtable->GetEnumeratedDesignBlock( m_nickname, m_dbname );
  39. if( design_block )
  40. {
  41. m_keywords = design_block->GetKeywords();
  42. m_doc = design_block->GetLibDescription();
  43. }
  44. m_loaded = true;
  45. }
  46. bool DESIGN_BLOCK_LIST_IMPL::CatchErrors( const std::function<void()>& aFunc )
  47. {
  48. try
  49. {
  50. aFunc();
  51. }
  52. catch( const IO_ERROR& ioe )
  53. {
  54. m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
  55. return false;
  56. }
  57. catch( const std::exception& se )
  58. {
  59. // This is a round about way to do this, but who knows what THROW_IO_ERROR()
  60. // may be tricked out to do someday, keep it in the game.
  61. try
  62. {
  63. THROW_IO_ERROR( se.what() );
  64. }
  65. catch( const IO_ERROR& ioe )
  66. {
  67. m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
  68. }
  69. return false;
  70. }
  71. return true;
  72. }
  73. bool DESIGN_BLOCK_LIST_IMPL::ReadDesignBlockFiles( DESIGN_BLOCK_LIB_TABLE* aTable,
  74. const wxString* aNickname,
  75. PROGRESS_REPORTER* aProgressReporter )
  76. {
  77. long long int generatedTimestamp = 0;
  78. if( !CatchErrors(
  79. [&]()
  80. {
  81. generatedTimestamp = aTable->GenerateTimestamp( aNickname );
  82. } ) )
  83. {
  84. return false;
  85. }
  86. if( generatedTimestamp == m_list_timestamp )
  87. return true;
  88. // Disable KIID generation: not needed for library parts; sometimes very slow
  89. KIID_NIL_SET_RESET reset_kiid;
  90. m_progress_reporter = aProgressReporter;
  91. if( m_progress_reporter )
  92. {
  93. m_progress_reporter->SetMaxProgress( m_queue_in.size() );
  94. m_progress_reporter->Report( _( "Fetching design_block libraries..." ) );
  95. }
  96. m_cancelled = false;
  97. m_lib_table = aTable;
  98. // Clear data before reading files
  99. m_errors.clear();
  100. m_list.clear();
  101. m_queue_in.clear();
  102. m_queue_out.clear();
  103. if( aNickname )
  104. {
  105. m_queue_in.push( *aNickname );
  106. }
  107. else
  108. {
  109. for( const wxString& nickname : aTable->GetLogicalLibs() )
  110. m_queue_in.push( nickname );
  111. }
  112. loadLibs();
  113. if( !m_cancelled )
  114. {
  115. if( m_progress_reporter )
  116. {
  117. m_progress_reporter->SetMaxProgress( m_queue_out.size() );
  118. m_progress_reporter->AdvancePhase();
  119. m_progress_reporter->Report( _( "Loading design_blocks..." ) );
  120. }
  121. loadDesignBlocks();
  122. if( m_progress_reporter )
  123. m_progress_reporter->AdvancePhase();
  124. }
  125. if( m_cancelled )
  126. m_list_timestamp = 0; // God knows what we got before we were canceled
  127. else
  128. m_list_timestamp = generatedTimestamp;
  129. return m_errors.empty();
  130. }
  131. void DESIGN_BLOCK_LIST_IMPL::loadLibs()
  132. {
  133. thread_pool& tp = GetKiCadThreadPool();
  134. size_t num_returns = m_queue_in.size();
  135. std::vector<std::future<size_t>> returns( num_returns );
  136. auto loader_job =
  137. [this]() -> size_t
  138. {
  139. wxString nickname;
  140. size_t retval = 0;
  141. if( !m_cancelled && m_queue_in.pop( nickname ) )
  142. {
  143. if( CatchErrors( [this, &nickname]()
  144. {
  145. m_lib_table->PrefetchLib( nickname );
  146. m_queue_out.push( nickname );
  147. } ) && m_progress_reporter )
  148. {
  149. m_progress_reporter->AdvanceProgress();
  150. }
  151. ++retval;
  152. }
  153. return retval;
  154. };
  155. for( size_t ii = 0; ii < num_returns; ++ii )
  156. returns[ii] = tp.submit( loader_job );
  157. for( const std::future<size_t>& ret : returns )
  158. {
  159. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  160. while( status != std::future_status::ready )
  161. {
  162. if( m_progress_reporter && !m_progress_reporter->KeepRefreshing() )
  163. m_cancelled = true;
  164. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  165. }
  166. }
  167. }
  168. void DESIGN_BLOCK_LIST_IMPL::loadDesignBlocks()
  169. {
  170. LOCALE_IO toggle_locale;
  171. // Parse the design_blocks in parallel. WARNING! This requires changing the locale, which is
  172. // GLOBAL. It is only thread safe to construct the LOCALE_IO before the threads are created,
  173. // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
  174. // from this will cause nasal demons.
  175. //
  176. // TODO: blast LOCALE_IO into the sun
  177. SYNC_QUEUE<std::unique_ptr<DESIGN_BLOCK_INFO>> queue_parsed;
  178. thread_pool& tp = GetKiCadThreadPool();
  179. size_t num_elements = m_queue_out.size();
  180. std::vector<std::future<size_t>> returns( num_elements );
  181. auto db_thread =
  182. [ this, &queue_parsed ]() -> size_t
  183. {
  184. wxString nickname;
  185. if( m_cancelled || !m_queue_out.pop( nickname ) )
  186. return 0;
  187. wxArrayString dbnames;
  188. CatchErrors(
  189. [&]()
  190. {
  191. m_lib_table->DesignBlockEnumerate( dbnames, nickname, false );
  192. } );
  193. for( wxString dbname : dbnames )
  194. {
  195. CatchErrors(
  196. [&]()
  197. {
  198. auto* dbinfo = new DESIGN_BLOCK_INFO_IMPL( this, nickname, dbname );
  199. queue_parsed.move_push( std::unique_ptr<DESIGN_BLOCK_INFO>( dbinfo ) );
  200. } );
  201. if( m_cancelled )
  202. return 0;
  203. }
  204. if( m_progress_reporter )
  205. m_progress_reporter->AdvanceProgress();
  206. return 1;
  207. };
  208. for( size_t ii = 0; ii < num_elements; ++ii )
  209. returns[ii] = tp.submit( db_thread );
  210. for( const std::future<size_t>& ret : returns )
  211. {
  212. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  213. while( status != std::future_status::ready )
  214. {
  215. if( m_progress_reporter )
  216. m_progress_reporter->KeepRefreshing();
  217. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  218. }
  219. }
  220. std::unique_ptr<DESIGN_BLOCK_INFO> dbi;
  221. while( queue_parsed.pop( dbi ) )
  222. m_list.push_back( std::move( dbi ) );
  223. std::sort( m_list.begin(), m_list.end(),
  224. []( std::unique_ptr<DESIGN_BLOCK_INFO> const& lhs,
  225. std::unique_ptr<DESIGN_BLOCK_INFO> const& rhs ) -> bool
  226. {
  227. return *lhs < *rhs;
  228. } );
  229. }
  230. DESIGN_BLOCK_LIST_IMPL::DESIGN_BLOCK_LIST_IMPL() :
  231. m_list_timestamp( 0 ), m_progress_reporter( nullptr ), m_cancelled( false )
  232. {
  233. }