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.

378 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2022 KiCad Developers.
  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 <board_design_settings.h>
  24. #include <drc/drc_engine.h>
  25. #include <drc/drc_item.h>
  26. #include <drc/drc_rule.h>
  27. #include <drc/drc_rule_condition.h>
  28. #include <drc/drc_test_provider.h>
  29. #include <pad.h>
  30. #include <pcb_track.h>
  31. #include <drawing_sheet/ds_draw_item.h>
  32. #include <drawing_sheet/ds_proxy_view_item.h>
  33. /*
  34. Miscellaneous tests:
  35. - DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
  36. - DRCE_INVALID_OUTLINE, ///< invalid board outline
  37. - DRCE_UNRESOLVED_VARIABLE,
  38. - DRCE_ASSERTION_FAILURE ///< user-defined assertions
  39. */
  40. class DRC_TEST_PROVIDER_MISC : public DRC_TEST_PROVIDER
  41. {
  42. public:
  43. DRC_TEST_PROVIDER_MISC() :
  44. m_board( nullptr )
  45. {
  46. m_isRuleDriven = false;
  47. }
  48. virtual ~DRC_TEST_PROVIDER_MISC()
  49. {
  50. }
  51. virtual bool Run() override;
  52. virtual const wxString GetName() const override
  53. {
  54. return wxT( "miscellaneous" );
  55. };
  56. virtual const wxString GetDescription() const override
  57. {
  58. return wxT( "Misc checks (board outline, missing textvars)" );
  59. }
  60. private:
  61. void testOutline();
  62. void testDisabledLayers();
  63. void testTextVars();
  64. void testAssertions();
  65. BOARD* m_board;
  66. };
  67. void DRC_TEST_PROVIDER_MISC::testOutline()
  68. {
  69. SHAPE_POLY_SET dummyOutline;
  70. bool errorHandled = false;
  71. OUTLINE_ERROR_HANDLER errorHandler =
  72. [&]( const wxString& msg, BOARD_ITEM* itemA, BOARD_ITEM* itemB, const VECTOR2I& pt )
  73. {
  74. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
  75. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  76. drcItem->SetItems( itemA, itemB );
  77. reportViolation( drcItem, pt, Edge_Cuts );
  78. errorHandled = true;
  79. };
  80. // Use a really tight chaining epsilon here so that we report errors that might affect
  81. // other tools (such as STEP export).
  82. constexpr int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ) / 100;
  83. if( !BuildBoardPolygonOutlines( m_board, dummyOutline, m_board->GetDesignSettings().m_MaxError,
  84. chainingEpsilon, &errorHandler ) )
  85. {
  86. if( errorHandled )
  87. {
  88. // if there is an invalid outline, then there must be an outline
  89. }
  90. else
  91. {
  92. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
  93. wxString msg;
  94. msg.Printf( _( "(no edges found on Edge.Cuts layer)" ) );
  95. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  96. drcItem->SetItems( m_board );
  97. reportViolation( drcItem, m_board->GetBoundingBox().Centre(), Edge_Cuts );
  98. }
  99. }
  100. }
  101. void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
  102. {
  103. const int progressDelta = 2000;
  104. int ii = 0;
  105. int items = 0;
  106. auto countItems =
  107. [&]( BOARD_ITEM* item ) -> bool
  108. {
  109. ++items;
  110. return true;
  111. };
  112. LSET disabledLayers = m_board->GetEnabledLayers().flip();
  113. // Perform the test only for copper layers
  114. disabledLayers &= LSET::AllCuMask();
  115. auto checkDisabledLayers =
  116. [&]( BOARD_ITEM* item ) -> bool
  117. {
  118. if( m_drcEngine->IsErrorLimitExceeded( DRCE_DISABLED_LAYER_ITEM ) )
  119. return false;
  120. if( !reportProgress( ii++, items, progressDelta ) )
  121. return false;
  122. PCB_LAYER_ID badLayer = UNDEFINED_LAYER;
  123. if( item->Type() == PCB_PAD_T )
  124. {
  125. PAD* pad = static_cast<PAD*>( item );
  126. if( pad->GetAttribute() == PAD_ATTRIB::SMD
  127. || pad->GetAttribute() == PAD_ATTRIB::CONN )
  128. {
  129. if( disabledLayers.test( pad->GetPrincipalLayer() ) )
  130. badLayer = item->GetLayer();
  131. }
  132. else
  133. {
  134. // Through hole pad pierces all physical layers.
  135. }
  136. }
  137. else if( item->Type() == PCB_VIA_T )
  138. {
  139. PCB_VIA* via = static_cast<PCB_VIA*>( item );
  140. PCB_LAYER_ID top;
  141. PCB_LAYER_ID bottom;
  142. via->LayerPair( &top, &bottom );
  143. if( disabledLayers.test( top ) )
  144. badLayer = top;
  145. else if( disabledLayers.test( bottom ) )
  146. badLayer = bottom;
  147. }
  148. else if( item->Type() == PCB_FP_ZONE_T )
  149. {
  150. // Footprint zones just get a top/bottom/inner setting, so they're on
  151. // whatever inner layers there are.
  152. }
  153. else
  154. {
  155. LSET badLayers = disabledLayers & item->GetLayerSet();
  156. if( badLayers.any() )
  157. badLayer = badLayers.Seq().front();
  158. }
  159. if( badLayer != UNDEFINED_LAYER )
  160. {
  161. auto drcItem = DRC_ITEM::Create( DRCE_DISABLED_LAYER_ITEM );
  162. wxString msg;
  163. msg.Printf( _( "(layer %s)" ), LayerName( badLayer ) );
  164. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
  165. drcItem->SetItems( item );
  166. reportViolation( drcItem, item->GetPosition(), UNDEFINED_LAYER );
  167. }
  168. return true;
  169. };
  170. forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), countItems );
  171. forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), checkDisabledLayers );
  172. }
  173. void DRC_TEST_PROVIDER_MISC::testAssertions()
  174. {
  175. const int progressDelta = 2000;
  176. int ii = 0;
  177. int items = 0;
  178. auto countItems =
  179. [&]( BOARD_ITEM* item ) -> bool
  180. {
  181. ++items;
  182. return true;
  183. };
  184. auto checkAssertions =
  185. [&]( BOARD_ITEM* item ) -> bool
  186. {
  187. if( m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
  188. return false;
  189. if( !reportProgress( ii++, items, progressDelta ) )
  190. return false;
  191. m_drcEngine->ProcessAssertions( item,
  192. [&]( const DRC_CONSTRAINT* c )
  193. {
  194. auto drcItem = DRC_ITEM::Create( DRCE_ASSERTION_FAILURE );
  195. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " (" )
  196. + c->GetName() + wxS( ")" ) );
  197. drcItem->SetItems( item );
  198. reportViolation( drcItem, item->GetPosition(), item->GetLayer() );
  199. } );
  200. return true;
  201. };
  202. forEachGeometryItem( {}, LSET::AllLayersMask(), countItems );
  203. forEachGeometryItem( {}, LSET::AllLayersMask(), checkAssertions );
  204. }
  205. void DRC_TEST_PROVIDER_MISC::testTextVars()
  206. {
  207. const int progressDelta = 2000;
  208. int ii = 0;
  209. int items = 0;
  210. static const std::vector<KICAD_T> itemTypes = {
  211. PCB_TEXT_T, PCB_FP_TEXT_T, PCB_TEXTBOX_T, PCB_FP_TEXTBOX_T,
  212. PCB_DIMENSION_T
  213. };
  214. forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
  215. [&]( BOARD_ITEM* item ) -> bool
  216. {
  217. ++items;
  218. return true;
  219. } );
  220. forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
  221. [&]( BOARD_ITEM* item ) -> bool
  222. {
  223. if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
  224. return false;
  225. if( !reportProgress( ii++, items, progressDelta ) )
  226. return false;
  227. BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
  228. EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( boardItem );
  229. wxCHECK( boardItem, false );
  230. if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) )
  231. {
  232. std::shared_ptr<DRC_ITEM>drcItem = DRC_ITEM::Create( DRCE_UNRESOLVED_VARIABLE );
  233. drcItem->SetItems( item );
  234. reportViolation( drcItem, boardItem->GetPosition(), boardItem->GetLayer() );
  235. }
  236. return true;
  237. } );
  238. DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet();
  239. DS_DRAW_ITEM_LIST drawItems;
  240. if( !drawingSheet || m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
  241. return;
  242. drawItems.SetMilsToIUfactor( pcbIUScale.IU_PER_MILS );
  243. drawItems.SetPageNumber( wxT( "1" ) );
  244. drawItems.SetSheetCount( 1 );
  245. drawItems.SetFileName( wxT( "dummyFilename" ) );
  246. drawItems.SetSheetName( wxT( "dummySheet" ) );
  247. drawItems.SetSheetLayer( wxT( "dummyLayer" ) );
  248. drawItems.SetProject( m_board->GetProject() );
  249. drawItems.BuildDrawItemsList( drawingSheet->GetPageInfo(), drawingSheet->GetTitleBlock() );
  250. for( DS_DRAW_ITEM_BASE* item = drawItems.GetFirst(); item; item = drawItems.GetNext() )
  251. {
  252. if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
  253. break;
  254. if( m_drcEngine->IsCancelled() )
  255. return;
  256. DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item );
  257. if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) )
  258. {
  259. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_UNRESOLVED_VARIABLE );
  260. drcItem->SetItems( drawingSheet );
  261. reportViolation( drcItem, text->GetPosition(), LAYER_DRAWINGSHEET );
  262. }
  263. }
  264. }
  265. bool DRC_TEST_PROVIDER_MISC::Run()
  266. {
  267. m_board = m_drcEngine->GetBoard();
  268. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_INVALID_OUTLINE ) )
  269. {
  270. if( !reportPhase( _( "Checking board outline..." ) ) )
  271. return false; // DRC cancelled
  272. testOutline();
  273. }
  274. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_DISABLED_LAYER_ITEM ) )
  275. {
  276. if( !reportPhase( _( "Checking disabled layers..." ) ) )
  277. return false; // DRC cancelled
  278. testDisabledLayers();
  279. }
  280. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
  281. {
  282. if( !reportPhase( _( "Checking text variables..." ) ) )
  283. return false; // DRC cancelled
  284. testTextVars();
  285. }
  286. if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
  287. {
  288. if( !reportPhase( _( "Checking assertions..." ) ) )
  289. return false; // DRC cancelled
  290. testAssertions();
  291. }
  292. return !m_drcEngine->IsCancelled();
  293. }
  294. namespace detail
  295. {
  296. static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_MISC> dummy;
  297. }