ADDED assertion constraints for user-defined DRC checks.

This commit is contained in:
Jeff Young 2021-09-22 22:20:18 +01:00
parent f7721dd274
commit b7e196b710
11 changed files with 302 additions and 38 deletions

View File

@ -1,4 +1,5 @@
annular_width annular_width
assertion
board_edge board_edge
buried_via buried_via
clearance clearance

View File

@ -292,6 +292,7 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
else if( sexprs.top() == "constraint" ) else if( sexprs.top() == "constraint" )
{ {
tokens = "annular_width|" tokens = "annular_width|"
"assertion|"
"clearance|" "clearance|"
"courtyard_clearance|" "courtyard_clearance|"
"diff_pair_gap|" "diff_pair_gap|"
@ -330,13 +331,11 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
} }
else if( sexprs.top() == "zone_connection" ) else if( sexprs.top() == "zone_connection" )
{ {
tokens = "solid " tokens = "none|solid|thermal_relief";
"thermal_relief "
"none";
} }
else if( sexprs.top() == "min_resolved_spokes" ) else if( sexprs.top() == "min_resolved_spokes" )
{ {
tokens = "0 1 2 3 4"; tokens = "0|1|2|3|4";
} }
else if( sexprs.top() == "layer" ) else if( sexprs.top() == "layer" )
{ {
@ -346,15 +345,14 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
{ {
tokens = "warning|error|ignore|exclusion"; tokens = "warning|error|ignore|exclusion";
} }
else if( sexprs.top() == "severity" )
{
tokens = "error "
"exclusion "
"ignore "
"warning";
}
} }
else if( context == STRING && !sexprs.empty() && sexprs.top() == "condition" ) else if( context == SEXPR_STRING && !sexprs.empty()
&& ( sexprs.top() == "condition" || sexprs.top() == "assertion" ) )
{
m_textEditor->AddText( "\"" );
}
else if( context == STRING && !sexprs.empty()
&& ( sexprs.top() == "condition" || sexprs.top() == "assertion" ) )
{ {
if( expr_context == STRUCT_REF ) if( expr_context == STRUCT_REF )
{ {

View File

@ -194,25 +194,25 @@ For the latter use a `(layer "layer_name")` clause in the rule.
(rule "Distance between Vias of Different Nets" (rule "Distance between Vias of Different Nets"
(constraint hole_to_hole (min 0.254mm)) (constraint hole_to_hole (min 0.254mm))
(condition "A.Type =='Via' && B.Type =='Via' && A.Net != B.Net")) (condition "A.Type == 'Via' && B.Type == 'Via' && A.Net != B.Net"))
(rule "Clearance between Pads of Different Nets" (rule "Clearance between Pads of Different Nets"
(constraint clearance (min 3.0mm)) (constraint clearance (min 3.0mm))
(condition "A.Type =='Pad' && B.Type =='Pad' && A.Net != B.Net")) (condition "A.Type == 'Pad' && B.Type == 'Pad' && A.Net != B.Net"))
(rule "Via Hole to Track Clearance" (rule "Via Hole to Track Clearance"
(constraint hole_clearance (min 0.254mm)) (constraint hole_clearance (min 0.254mm))
(condition "A.Type =='Via' && B.Type =='Track'")) (condition "A.Type == 'Via' && B.Type == 'Track'"))
(rule "Pad to Track Clearance" (rule "Pad to Track Clearance"
(constraint clearance (min 0.2mm)) (constraint clearance (min 0.2mm))
(condition "A.Type =='Pad' && B.Type =='Track'")) (condition "A.Type == 'Pad' && B.Type == 'Track'"))
(rule "clearance-to-1mm-cutout" (rule "clearance-to-1mm-cutout"
(constraint clearance (min 0.8mm)) (constraint clearance (min 0.8mm))
(condition "A.Layer=='Edge.Cuts' && A.Thickness == 1.0mm")) (condition "A.Layer == 'Edge.Cuts' && A.Thickness == 1.0mm"))
(rule "Max Drill Hole Size Mechanical" (rule "Max Drill Hole Size Mechanical"
@ -247,4 +247,10 @@ For the latter use a `(layer "layer_name")` clause in the rule.
# Prevent solder wicking from SMD pads # Prevent solder wicking from SMD pads
(rule holes_in_pads (rule holes_in_pads
(constraint mechanical_hole_clearance (min 0.2mm)) (constraint mechanical_hole_clearance (min 0.2mm))
(condition "B.Pad_Type == 'SMD'")) (condition "B.Pad_Type == 'SMD'"))
# Disallow solder mask margin overrides
(rule "disallow solder mask margin overrides"
(constraint assertion "A.Soldermask_Margin_Override == 0mm")
(condition "A.Type == 'Pad'"))

View File

@ -871,6 +871,18 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
} }
} }
auto testAssertion =
[&]( const DRC_ENGINE_CONSTRAINT* c )
{
REPORT( wxString::Format( _( "Checking assertion \"%s\"." ),
EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
if( c->constraint.m_Test->EvaluateFor( a, b, aLayer, aReporter ) )
REPORT( _( "Assertion passed." ) )
else
REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
};
auto processConstraint = auto processConstraint =
[&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool [&]( const DRC_ENGINE_CONSTRAINT* c ) -> bool
{ {
@ -1027,8 +1039,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
} }
else if( c->parentRule ) else if( c->parentRule )
{ {
REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule " REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
"ignored." ),
EscapeHTML( c->parentRule->m_LayerSource ) ) ) EscapeHTML( c->parentRule->m_LayerSource ) ) )
} }
else else
@ -1061,8 +1072,22 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
if( !c->condition || c->condition->GetExpression().IsEmpty() ) if( !c->condition || c->condition->GetExpression().IsEmpty() )
{ {
REPORT( implicit ? _( "Unconditional constraint applied." ) if( aReporter )
: _( "Unconditional rule applied." ) ); {
if( implicit )
{
REPORT( _( "Unconditional constraint applied." ) )
}
else if( constraint.m_Type == ASSERTION_CONSTRAINT )
{
REPORT( _( "Unconditional rule applied." ) )
testAssertion( c );
}
else
{
REPORT( _( "Unconditional rule applied; overrides previous constraints." ) )
}
}
constraint = c->constraint; constraint = c->constraint;
return true; return true;
@ -1081,8 +1106,22 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
if( c->condition->EvaluateFor( a, b, aLayer, aReporter ) ) if( c->condition->EvaluateFor( a, b, aLayer, aReporter ) )
{ {
REPORT( implicit ? _( "Constraint applied." ) if( aReporter )
: _( "Rule applied; overrides previous constraints." ) ) {
if( implicit )
{
REPORT( _( "Constraint applied." ) )
}
else if( constraint.m_Type == ASSERTION_CONSTRAINT )
{
REPORT( _( "Rule applied." ) )
testAssertion( c );
}
else
{
REPORT( _( "Rule applied; overrides previous constraints." ) )
}
}
if( c->constraint.m_Value.HasMin() ) if( c->constraint.m_Value.HasMin() )
constraint.m_Value.SetMin( c->constraint.m_Value.Min() ); constraint.m_Value.SetMin( c->constraint.m_Value.Min() );
@ -1239,6 +1278,78 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
return constraint; return constraint;
} }
void DRC_ENGINE::ProcessAssertions( const BOARD_ITEM* a,
std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
REPORTER* aReporter )
{
/*
* NOTE: all string manipulation MUST BE KEPT INSIDE the REPORT macro. It absolutely
* kills performance when running bulk DRC tests (where aReporter is nullptr).
*/
auto testAssertion =
[&]( const DRC_ENGINE_CONSTRAINT* c )
{
REPORT( wxString::Format( _( "Checking rule assertion \"%s\"." ),
EscapeHTML( c->constraint.m_Test->GetExpression() ) ) )
if( c->constraint.m_Test->EvaluateFor( a, nullptr, a->GetLayer(), aReporter ) )
{
REPORT( _( "Assertion passed." ) )
}
else
{
REPORT( EscapeHTML( _( "--> Assertion failed. <--" ) ) )
aFailureHandler( &c->constraint );
}
};
auto processConstraint =
[&]( const DRC_ENGINE_CONSTRAINT* c )
{
REPORT( "" )
REPORT( wxString::Format( _( "Checking %s." ), c->constraint.GetName() ) )
if( !( a->GetLayerSet() & c->layerTest ).any() )
{
REPORT( wxString::Format( _( "Rule layer '%s' not matched; rule ignored." ),
EscapeHTML( c->parentRule->m_LayerSource ) ) )
}
if( !c->condition || c->condition->GetExpression().IsEmpty() )
{
REPORT( _( "Unconditional rule applied." ) )
testAssertion( c );
}
else
{
REPORT( wxString::Format( _( "Checking rule condition \"%s\"." ),
EscapeHTML( c->condition->GetExpression() ) ) )
if( c->condition->EvaluateFor( a, nullptr, a->GetLayer(), aReporter ) )
{
REPORT( _( "Rule applied." ) )
testAssertion( c );
}
else
{
REPORT( _( "Condition not satisfied; rule ignored." ) )
}
}
};
if( m_constraintMap.count( ASSERTION_CONSTRAINT ) )
{
std::vector<DRC_ENGINE_CONSTRAINT*>* ruleset = m_constraintMap[ ASSERTION_CONSTRAINT ];
for( int ii = 0; ii < (int) ruleset->size(); ++ii )
processConstraint( ruleset->at( ii ) );
}
}
#undef REPORT #undef REPORT
#undef UNITS #undef UNITS
#undef REPORT_VALUE #undef REPORT_VALUE

View File

@ -152,6 +152,10 @@ public:
DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b, DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr ); PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr );
void ProcessAssertions( const BOARD_ITEM* a,
std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
REPORTER* aReporter = nullptr );
bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID ); bool HasRulesForConstraintType( DRC_CONSTRAINT_T constraintID );
EDA_UNITS UserUnits() const { return m_userUnits; } EDA_UNITS UserUnits() const { return m_userUnits; }

View File

@ -183,6 +183,10 @@ DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE,
_( "Unresolved text variable" ), _( "Unresolved text variable" ),
wxT( "unresolved_variable" ) ); wxT( "unresolved_variable" ) );
DRC_ITEM DRC_ITEM::assertionFailure( DRCE_ASSERTION_FAILURE,
_( "Assertion failure" ),
wxT( "assertion_failure" ) );
DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER, DRC_ITEM DRC_ITEM::copperSliver( DRCE_COPPER_SLIVER,
_( "Copper sliver" ), _( "Copper sliver" ),
wxT( "copper_sliver" ) ); wxT( "copper_sliver" ) );
@ -334,6 +338,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint ); case DRCE_EXTRA_FOOTPRINT: return std::make_shared<DRC_ITEM>( extraFootprint );
case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared<DRC_ITEM>( libFootprintIssues ); case DRCE_LIB_FOOTPRINT_ISSUES: return std::make_shared<DRC_ITEM>( libFootprintIssues );
case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable ); case DRCE_UNRESOLVED_VARIABLE: return std::make_shared<DRC_ITEM>( unresolvedVariable );
case DRCE_ASSERTION_FAILURE: return std::make_shared<DRC_ITEM>( assertionFailure );
case DRCE_COPPER_SLIVER: return std::make_shared<DRC_ITEM>( copperSliver ); case DRCE_COPPER_SLIVER: return std::make_shared<DRC_ITEM>( copperSliver );
case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps ); case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps );
case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance ); case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance );

View File

@ -73,6 +73,7 @@ enum PCB_DRC_CODE {
DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole DRCE_PAD_TH_WITH_NO_HOLE, // footprint has Plated Through-Hole with no hole
DRCE_UNRESOLVED_VARIABLE, DRCE_UNRESOLVED_VARIABLE,
DRCE_ASSERTION_FAILURE, // user-defined (custom rule) assertion
DRCE_COPPER_SLIVER, DRCE_COPPER_SLIVER,
DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness
@ -177,6 +178,7 @@ private:
static DRC_ITEM netConflict; static DRC_ITEM netConflict;
static DRC_ITEM libFootprintIssues; static DRC_ITEM libFootprintIssues;
static DRC_ITEM unresolvedVariable; static DRC_ITEM unresolvedVariable;
static DRC_ITEM assertionFailure;
static DRC_ITEM copperSliver; static DRC_ITEM copperSliver;
static DRC_ITEM silkClearance; static DRC_ITEM silkClearance;
static DRC_ITEM solderMaskBridge; static DRC_ITEM solderMaskBridge;

View File

@ -67,7 +67,8 @@ enum DRC_CONSTRAINT_T
DIFF_PAIR_INTRA_SKEW_CONSTRAINT, DIFF_PAIR_INTRA_SKEW_CONSTRAINT,
VIA_COUNT_CONSTRAINT, VIA_COUNT_CONSTRAINT,
MECHANICAL_CLEARANCE_CONSTRAINT, MECHANICAL_CLEARANCE_CONSTRAINT,
MECHANICAL_HOLE_CLEARANCE_CONSTRAINT MECHANICAL_HOLE_CLEARANCE_CONSTRAINT,
ASSERTION_CONSTRAINT
}; };
@ -123,6 +124,7 @@ class DRC_CONSTRAINT
m_Value(), m_Value(),
m_DisallowFlags( 0 ), m_DisallowFlags( 0 ),
m_ZoneConnection( ZONE_CONNECTION::INHERITED ), m_ZoneConnection( ZONE_CONNECTION::INHERITED ),
m_Test( nullptr ),
m_name( aName ), m_name( aName ),
m_parentRule( nullptr ) m_parentRule( nullptr )
{ {
@ -163,14 +165,15 @@ class DRC_CONSTRAINT
} }
public: public:
DRC_CONSTRAINT_T m_Type; DRC_CONSTRAINT_T m_Type;
MINOPTMAX<int> m_Value; MINOPTMAX<int> m_Value;
int m_DisallowFlags; int m_DisallowFlags;
ZONE_CONNECTION m_ZoneConnection; ZONE_CONNECTION m_ZoneConnection;
DRC_RULE_CONDITION* m_Test;
private: private:
wxString m_name; // For just-in-time constraints wxString m_name; // For just-in-time constraints
DRC_RULE* m_parentRule; // For constraints found in rules DRC_RULE* m_parentRule; // For constraints found in rules
}; };

View File

@ -260,7 +260,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
if( (int) token == DSN_RIGHT || token == T_EOF ) if( (int) token == DSN_RIGHT || token == T_EOF )
{ {
msg.Printf( _( "Missing constraint type.| Expected %s." ), msg.Printf( _( "Missing constraint type.| Expected %s." ),
"clearance, hole_clearance, edge_clearance, mechanical_clearance, " "assertion, clearance, hole_clearance, edge_clearance, mechanical_clearance, "
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, " "mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
"hole_to_hole, track_width, annular_width, via_diameter, disallow, " "hole_to_hole, track_width, annular_width, via_diameter, disallow, "
"zone_connection, thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, " "zone_connection, thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, "
@ -271,6 +271,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
switch( token ) switch( token )
{ {
case T_assertion: c.m_Type = ASSERTION_CONSTRAINT; break;
case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break; case T_clearance: c.m_Type = CLEARANCE_CONSTRAINT; break;
case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break; case T_hole_clearance: c.m_Type = HOLE_CLEARANCE_CONSTRAINT; break;
case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break; case T_edge_clearance: c.m_Type = EDGE_CLEARANCE_CONSTRAINT; break;
@ -298,7 +299,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
case T_mechanical_hole_clearance: c.m_Type = MECHANICAL_HOLE_CLEARANCE_CONSTRAINT; break; case T_mechanical_hole_clearance: c.m_Type = MECHANICAL_HOLE_CLEARANCE_CONSTRAINT; break;
default: default:
msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(), msg.Printf( _( "Unrecognized item '%s'.| Expected %s." ), FromUTF8(),
"clearance, hole_clearance, edge_clearance, mechanical_clearance, " "assertion, clearance, hole_clearance, edge_clearance, mechanical_clearance, "
"mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, " "mechanical_hole_clearance, courtyard_clearance, silk_clearance, hole_size, "
"hole_to_hole, track_width, annular_width, disallow, zone_connection, " "hole_to_hole, track_width, annular_width, disallow, zone_connection, "
"thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, length, skew, " "thermal_relief_gap, thermal_spoke_width, min_resolved_spokes, length, skew, "
@ -412,6 +413,33 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
aRule->AddConstraint( c ); aRule->AddConstraint( c );
return; return;
} }
else if( c.m_Type == ASSERTION_CONSTRAINT )
{
token = NextTok();
if( (int) token == DSN_RIGHT )
reportError( _( "Missing assertion expression." ) );
if( IsSymbol( token ) )
{
c.m_Test = new DRC_RULE_CONDITION( FromUTF8() );
c.m_Test->Compile( m_reporter, CurLineNumber(), CurOffset() );
}
else
{
msg.Printf( _( "Unrecognized item '%s'.| Expected quoted expression." ), FromUTF8() );
reportError( msg );
}
if( (int) NextTok() != DSN_RIGHT )
{
reportError( wxString::Format( _( "Unrecognized item '%s'." ), FromUTF8() ) );
parseUnknown();
}
aRule->AddConstraint( c );
return;
}
for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() ) for( token = NextTok(); token != T_RIGHT && token != T_EOF; token = NextTok() )
{ {

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2004-2020 KiCad Developers. * Copyright (C) 2004-2021 KiCad Developers.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -25,6 +25,7 @@
#include <drc/drc_engine.h> #include <drc/drc_engine.h>
#include <drc/drc_item.h> #include <drc/drc_item.h>
#include <drc/drc_rule.h> #include <drc/drc_rule.h>
#include <drc/drc_rule_condition.h>
#include <drc/drc_test_provider.h> #include <drc/drc_test_provider.h>
#include <pad.h> #include <pad.h>
#include <pcb_track.h> #include <pcb_track.h>
@ -38,6 +39,7 @@
- DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer - DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
- DRCE_INVALID_OUTLINE, ///< invalid board outline - DRCE_INVALID_OUTLINE, ///< invalid board outline
- DRCE_UNRESOLVED_VARIABLE, - DRCE_UNRESOLVED_VARIABLE,
- DRCE_ASSERTION_FAILURE ///< user-defined assertions
*/ */
class DRC_TEST_PROVIDER_MISC : public DRC_TEST_PROVIDER class DRC_TEST_PROVIDER_MISC : public DRC_TEST_PROVIDER
@ -69,6 +71,7 @@ private:
void testOutline(); void testOutline();
void testDisabledLayers(); void testDisabledLayers();
void testTextVars(); void testTextVars();
void testAssertions();
BOARD* m_board; BOARD* m_board;
}; };
@ -119,6 +122,19 @@ void DRC_TEST_PROVIDER_MISC::testOutline()
void DRC_TEST_PROVIDER_MISC::testDisabledLayers() void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
{ {
// This is the number of tests between 2 calls to the progress bar
const int delta = 2000;
int ii = 0;
int items = 0;
auto countItems =
[&]( BOARD_ITEM* item ) -> bool
{
++items;
return true;
};
LSET disabledLayers = m_board->GetEnabledLayers().flip(); LSET disabledLayers = m_board->GetEnabledLayers().flip();
// Perform the test only for copper layers // Perform the test only for copper layers
@ -127,6 +143,12 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
auto checkDisabledLayers = auto checkDisabledLayers =
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
{ {
if( m_drcEngine->IsErrorLimitExceeded( DRCE_DISABLED_LAYER_ITEM ) )
return false;
if( !reportProgress( ii++, items, delta ) )
return false;
PCB_LAYER_ID badLayer = UNDEFINED_LAYER; PCB_LAYER_ID badLayer = UNDEFINED_LAYER;
if( item->Type() == PCB_PAD_T ) if( item->Type() == PCB_PAD_T )
@ -172,7 +194,7 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
if( badLayer != UNDEFINED_LAYER ) if( badLayer != UNDEFINED_LAYER )
{ {
std::shared_ptr<DRC_ITEM>drcItem = DRC_ITEM::Create( DRCE_DISABLED_LAYER_ITEM ); auto drcItem = DRC_ITEM::Create( DRCE_DISABLED_LAYER_ITEM );
m_msg.Printf( _( "(layer %s)" ), LayerName( badLayer ) ); m_msg.Printf( _( "(layer %s)" ), LayerName( badLayer ) );
@ -185,18 +207,78 @@ void DRC_TEST_PROVIDER_MISC::testDisabledLayers()
return true; return true;
}; };
forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), countItems );
forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), checkDisabledLayers ); forEachGeometryItem( s_allBasicItems, LSET::AllLayersMask(), checkDisabledLayers );
} }
void DRC_TEST_PROVIDER_MISC::testAssertions()
{
// This is the number of tests between 2 calls to the progress bar
const int delta = 2000;
int ii = 0;
int items = 0;
auto countItems =
[&]( BOARD_ITEM* item ) -> bool
{
++items;
return true;
};
auto checkAssertions =
[&]( BOARD_ITEM* item ) -> bool
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
return false;
if( !reportProgress( ii++, items, delta ) )
return false;
m_drcEngine->ProcessAssertions( item,
[&]( const DRC_CONSTRAINT* c )
{
auto drcItem = DRC_ITEM::Create( DRCE_ASSERTION_FAILURE );
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " (" )
+ c->GetName() + wxS( ")" ) );
drcItem->SetItems( item );
reportViolation( drcItem, item->GetPosition() );
} );
return true;
};
forEachGeometryItem( {}, LSET::AllLayersMask(), countItems );
forEachGeometryItem( {}, LSET::AllLayersMask(), checkAssertions );
}
void DRC_TEST_PROVIDER_MISC::testTextVars() void DRC_TEST_PROVIDER_MISC::testTextVars()
{ {
auto checkUnresolvedTextVar = // This is the number of tests between 2 calls to the progress bar
const int delta = 2000;
int ii = 0;
int items = 0;
auto countItems =
[&]( BOARD_ITEM* item ) -> bool
{
++items;
return true;
};
auto checkTextVars =
[&]( EDA_ITEM* item ) -> bool [&]( EDA_ITEM* item ) -> bool
{ {
if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) ) if( m_drcEngine->IsErrorLimitExceeded( DRCE_UNRESOLVED_VARIABLE ) )
return false; return false;
if( !reportProgress( ii++, items, delta ) )
return false;
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ); EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item );
if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) ) if( text && text->GetShownText().Matches( wxT( "*${*}*" ) ) )
@ -209,8 +291,8 @@ void DRC_TEST_PROVIDER_MISC::testTextVars()
return true; return true;
}; };
forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(), forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(), countItems );
checkUnresolvedTextVar ); forEachGeometryItem( { PCB_FP_TEXT_T, PCB_TEXT_T }, LSET::AllLayersMask(), checkTextVars );
DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet(); DS_PROXY_VIEW_ITEM* drawingSheet = m_drcEngine->GetDrawingSheet();
DS_DRAW_ITEM_LIST drawItems; DS_DRAW_ITEM_LIST drawItems;
@ -273,6 +355,14 @@ bool DRC_TEST_PROVIDER_MISC::Run()
testTextVars(); testTextVars();
} }
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ASSERTION_FAILURE ) )
{
if( !reportPhase( _( "Checking assertions..." ) ) )
return false; // DRC cancelled
testAssertions();
}
return true; return true;
} }

View File

@ -840,6 +840,22 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
r->Flush(); r->Flush();
r = m_inspectConstraintsDialog->AddPage( _( "Assertions" ) );
reportHeader( _( "Assertions for:" ), item, r );
if( compileError )
reportCompileError( r );
if( courtyardError )
{
r->Report( "" );
r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
+ " <a href='drc'>" + _( "Run DRC for a full analysis." ) + "</a>" );
}
drcEngine.ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
r->Flush();
m_inspectConstraintsDialog->FinishInitialization(); m_inspectConstraintsDialog->FinishInitialization();
m_inspectConstraintsDialog->Raise(); m_inspectConstraintsDialog->Raise();
m_inspectConstraintsDialog->Show( true ); m_inspectConstraintsDialog->Show( true );