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.

385 lines
10 KiB

5 years ago
5 years ago
4 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020-2024 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. #include <drc/drc_engine.h>
  24. #include <drc/drc_item.h>
  25. #include <drc/drc_test_provider.h>
  26. #include <pcb_track.h>
  27. #include <footprint.h>
  28. #include <pad.h>
  29. #include <zone.h>
  30. #include <pcb_text.h>
  31. // A list of all basic (ie: non-compound) board geometry items
  32. std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItems;
  33. std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItemsButZones;
  34. DRC_TEST_PROVIDER_REGISTRY::~DRC_TEST_PROVIDER_REGISTRY()
  35. {
  36. for( DRC_TEST_PROVIDER* provider : m_providers )
  37. delete provider;
  38. }
  39. DRC_TEST_PROVIDER::DRC_TEST_PROVIDER() :
  40. UNITS_PROVIDER( pcbIUScale, EDA_UNITS::MILLIMETRES ),
  41. m_drcEngine( nullptr ),
  42. m_commit (nullptr )
  43. {
  44. }
  45. void DRC_TEST_PROVIDER::Init()
  46. {
  47. if( s_allBasicItems.size() == 0 )
  48. {
  49. for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
  50. {
  51. if( i != PCB_FOOTPRINT_T && i != PCB_GROUP_T )
  52. {
  53. s_allBasicItems.push_back( (KICAD_T) i );
  54. if( i != PCB_ZONE_T )
  55. s_allBasicItemsButZones.push_back( (KICAD_T) i );
  56. }
  57. }
  58. }
  59. }
  60. const wxString DRC_TEST_PROVIDER::GetName() const { return wxT( "<no name test>" ); }
  61. const wxString DRC_TEST_PROVIDER::GetDescription() const { return wxEmptyString; }
  62. void DRC_TEST_PROVIDER::reportViolation( std::shared_ptr<DRC_ITEM>& item,
  63. const VECTOR2I& aMarkerPos, int aMarkerLayer )
  64. {
  65. std::lock_guard<std::mutex> lock( m_statsMutex );
  66. if( item->GetViolatingRule() )
  67. accountCheck( item->GetViolatingRule() );
  68. item->SetViolatingTest( this );
  69. m_drcEngine->ReportViolation( item, aMarkerPos, aMarkerLayer );
  70. }
  71. bool DRC_TEST_PROVIDER::reportProgress( size_t aCount, size_t aSize, size_t aDelta )
  72. {
  73. if( ( aCount % aDelta ) == 0 || aCount == aSize - 1 )
  74. {
  75. if( !m_drcEngine->ReportProgress( static_cast<double>( aCount ) / aSize ) )
  76. return false;
  77. }
  78. return true;
  79. }
  80. bool DRC_TEST_PROVIDER::reportPhase( const wxString& aMessage )
  81. {
  82. reportAux( aMessage );
  83. return m_drcEngine->ReportPhase( aMessage );
  84. }
  85. void DRC_TEST_PROVIDER::reportAux( const wxChar* fmt, ... )
  86. {
  87. va_list vargs;
  88. va_start( vargs, fmt );
  89. wxString str;
  90. str.PrintfV( fmt, vargs );
  91. va_end( vargs );
  92. m_drcEngine->ReportAux( str );
  93. }
  94. void DRC_TEST_PROVIDER::accountCheck( const DRC_RULE* ruleToTest )
  95. {
  96. auto it = m_stats.find( ruleToTest );
  97. if( it == m_stats.end() )
  98. m_stats[ ruleToTest ] = 1;
  99. else
  100. m_stats[ ruleToTest ] += 1;
  101. }
  102. void DRC_TEST_PROVIDER::accountCheck( const DRC_CONSTRAINT& constraintToTest )
  103. {
  104. accountCheck( constraintToTest.GetParentRule() );
  105. }
  106. void DRC_TEST_PROVIDER::reportRuleStatistics()
  107. {
  108. if( !m_isRuleDriven )
  109. return;
  110. m_drcEngine->ReportAux( wxT( "Rule hit statistics: " ) );
  111. for( const std::pair<const DRC_RULE* const, int>& stat : m_stats )
  112. {
  113. if( stat.first )
  114. {
  115. m_drcEngine->ReportAux( wxString::Format( wxT( " - rule '%s': %d hits " ),
  116. stat.first->m_Name,
  117. stat.second ) );
  118. }
  119. }
  120. }
  121. int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes, LSET aLayers,
  122. const std::function<bool( BOARD_ITEM*)>& aFunc )
  123. {
  124. BOARD *brd = m_drcEngine->GetBoard();
  125. std::bitset<MAX_STRUCT_TYPE_ID> typeMask;
  126. int n = 0;
  127. if( aTypes.size() == 0 )
  128. {
  129. for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
  130. typeMask[ i ] = true;
  131. }
  132. else
  133. {
  134. for( KICAD_T aType : aTypes )
  135. typeMask[ aType ] = true;
  136. }
  137. for( PCB_TRACK* item : brd->Tracks() )
  138. {
  139. if( (item->GetLayerSet() & aLayers).any() )
  140. {
  141. if( typeMask[ PCB_TRACE_T ] && item->Type() == PCB_TRACE_T )
  142. {
  143. aFunc( item );
  144. n++;
  145. }
  146. else if( typeMask[ PCB_VIA_T ] && item->Type() == PCB_VIA_T )
  147. {
  148. aFunc( item );
  149. n++;
  150. }
  151. else if( typeMask[ PCB_ARC_T ] && item->Type() == PCB_ARC_T )
  152. {
  153. aFunc( item );
  154. n++;
  155. }
  156. }
  157. }
  158. for( BOARD_ITEM* item : brd->Drawings() )
  159. {
  160. if( (item->GetLayerSet() & aLayers).any() )
  161. {
  162. if( typeMask[ PCB_DIMENSION_T ] && BaseType( item->Type() ) == PCB_DIMENSION_T )
  163. {
  164. if( !aFunc( item ) )
  165. return n;
  166. n++;
  167. }
  168. else if( typeMask[ PCB_SHAPE_T ] && item->Type() == PCB_SHAPE_T )
  169. {
  170. if( !aFunc( item ) )
  171. return n;
  172. n++;
  173. }
  174. else if( typeMask[ PCB_TEXT_T ] && item->Type() == PCB_TEXT_T )
  175. {
  176. if( !aFunc( item ) )
  177. return n;
  178. n++;
  179. }
  180. else if( typeMask[ PCB_TEXTBOX_T ] && item->Type() == PCB_TEXTBOX_T )
  181. {
  182. if( !aFunc( item ) )
  183. return n;
  184. n++;
  185. }
  186. else if( typeMask[ PCB_TARGET_T ] && item->Type() == PCB_TARGET_T )
  187. {
  188. if( !aFunc( item ) )
  189. return n;
  190. n++;
  191. }
  192. }
  193. }
  194. if( typeMask[ PCB_ZONE_T ] )
  195. {
  196. for( ZONE* item : brd->Zones() )
  197. {
  198. if( ( item->GetLayerSet() & aLayers ).any() )
  199. {
  200. if( !aFunc( item ) )
  201. return n;
  202. n++;
  203. }
  204. }
  205. }
  206. for( FOOTPRINT* footprint : brd->Footprints() )
  207. {
  208. if( typeMask[ PCB_FIELD_T ] )
  209. {
  210. for( PCB_FIELD* field : footprint->GetFields() )
  211. {
  212. if( ( field->GetLayerSet() & aLayers ).any() )
  213. {
  214. if( !aFunc( field ) )
  215. return n;
  216. n++;
  217. }
  218. }
  219. }
  220. if( typeMask[ PCB_PAD_T ] )
  221. {
  222. for( PAD* pad : footprint->Pads() )
  223. {
  224. // Careful: if a pad has a hole then it pierces all layers
  225. if( pad->HasHole() || ( pad->GetLayerSet() & aLayers ).any() )
  226. {
  227. if( !aFunc( pad ) )
  228. return n;
  229. n++;
  230. }
  231. }
  232. }
  233. for( BOARD_ITEM* dwg : footprint->GraphicalItems() )
  234. {
  235. if( (dwg->GetLayerSet() & aLayers).any() )
  236. {
  237. if( typeMask[ PCB_DIMENSION_T ] && BaseType( dwg->Type() ) == PCB_DIMENSION_T )
  238. {
  239. if( !aFunc( dwg ) )
  240. return n;
  241. n++;
  242. }
  243. else if( typeMask[ PCB_TEXT_T ] && dwg->Type() == PCB_TEXT_T )
  244. {
  245. if( !aFunc( dwg ) )
  246. return n;
  247. n++;
  248. }
  249. else if( typeMask[ PCB_TEXTBOX_T ] && dwg->Type() == PCB_TEXTBOX_T )
  250. {
  251. if( !aFunc( dwg ) )
  252. return n;
  253. n++;
  254. }
  255. else if( typeMask[ PCB_SHAPE_T ] && dwg->Type() == PCB_SHAPE_T )
  256. {
  257. if( !aFunc( dwg ) )
  258. return n;
  259. n++;
  260. }
  261. }
  262. }
  263. if( typeMask[ PCB_ZONE_T ] )
  264. {
  265. for( ZONE* zone : footprint->Zones() )
  266. {
  267. if( (zone->GetLayerSet() & aLayers).any() )
  268. {
  269. if( !aFunc( zone ) )
  270. return n;
  271. n++;
  272. }
  273. }
  274. }
  275. if( typeMask[ PCB_FOOTPRINT_T ] )
  276. {
  277. if( !aFunc( footprint ) )
  278. return n;
  279. n++;
  280. }
  281. }
  282. return n;
  283. }
  284. bool DRC_TEST_PROVIDER::isInvisibleText( const BOARD_ITEM* aItem ) const
  285. {
  286. if( const PCB_TEXT* text = dynamic_cast<const PCB_TEXT*>( aItem ) )
  287. {
  288. if( !text->IsVisible() )
  289. return true;
  290. }
  291. return false;
  292. }
  293. wxString DRC_TEST_PROVIDER::formatMsg( const wxString& aFormatString, const wxString& aSource,
  294. double aConstraint, double aActual )
  295. {
  296. wxString constraint_str = MessageTextFromValue( aConstraint );
  297. wxString actual_str = MessageTextFromValue( aActual );
  298. if( constraint_str == actual_str )
  299. {
  300. // Use more precise formatting if the message-text strings were equal.
  301. constraint_str = StringFromValue( aConstraint, true );
  302. actual_str = StringFromValue( aActual, true );
  303. }
  304. return wxString::Format( aFormatString, aSource, constraint_str, actual_str );
  305. }
  306. wxString DRC_TEST_PROVIDER::formatMsg( const wxString& aFormatString, const wxString& aSource,
  307. const EDA_ANGLE& aConstraint, const EDA_ANGLE& aActual )
  308. {
  309. wxString constraint_str = MessageTextFromValue( aConstraint );
  310. wxString actual_str = MessageTextFromValue( aActual );
  311. if( constraint_str == actual_str )
  312. {
  313. // Use more precise formatting if the message-text strings were equal.
  314. constraint_str = StringFromValue( aConstraint, true );
  315. actual_str = StringFromValue( aActual, true );
  316. }
  317. return wxString::Format( aFormatString, aSource, constraint_str, actual_str );
  318. }