2020-06-13 23:28:08 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
|
|
* Copyright (C) 2014 Dick Hollenbeck, dick@softplc.com
|
|
|
|
* Copyright (C) 2017-2020 KiCad Developers, see change_log.txt for contributors.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
// fixme - way too much includes
|
2020-06-13 23:28:08 +00:00
|
|
|
#include <fctsys.h>
|
|
|
|
#include <pcbnew.h>
|
2020-06-18 16:55:22 +00:00
|
|
|
|
|
|
|
#include <reporter.h>
|
|
|
|
#include <widgets/progress_reporter.h>
|
2020-06-13 23:28:08 +00:00
|
|
|
|
|
|
|
#include <drc_proto/drc_engine.h>
|
|
|
|
#include <drc_proto/drc_rule_parser.h>
|
|
|
|
#include <drc_proto/drc_rule.h>
|
|
|
|
#include <drc_proto/drc_item.h>
|
|
|
|
#include <drc_proto/drc_test_provider.h>
|
|
|
|
|
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
void drcPrintDebugMessage( int level, wxString msg, const char *function, int line )
|
|
|
|
{
|
|
|
|
wxString valueStr;
|
|
|
|
if( wxGetEnv( "DRC_DEBUG", &valueStr ) )
|
|
|
|
{
|
|
|
|
int setLevel = wxAtoi( valueStr );
|
2020-08-25 17:42:52 +00:00
|
|
|
if( level <= setLevel )
|
|
|
|
{
|
|
|
|
printf("%-30s:%d | %s\n", function, line, (const char *) msg.c_str() );
|
|
|
|
}
|
2020-08-12 22:19:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
|
|
|
test::DRC_ENGINE::DRC_ENGINE( BOARD* aBoard, BOARD_DESIGN_SETTINGS *aSettings ) :
|
2020-06-13 23:28:08 +00:00
|
|
|
m_board( aBoard ),
|
2020-06-19 21:34:19 +00:00
|
|
|
m_designSettings ( aSettings ),
|
2020-08-25 17:42:52 +00:00
|
|
|
m_worksheet( nullptr ),
|
|
|
|
m_schematicNetlist( nullptr ),
|
2020-06-19 21:34:19 +00:00
|
|
|
m_reporter( nullptr ),
|
|
|
|
m_progressReporter( nullptr )
|
2020-08-18 14:17:16 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2020-06-13 23:28:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
test::DRC_ENGINE::~DRC_ENGINE()
|
|
|
|
{
|
2020-08-11 22:15:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-08-11 22:15:50 +00:00
|
|
|
test::DRC_REPORT::~DRC_REPORT()
|
|
|
|
{
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-13 23:28:08 +00:00
|
|
|
/*void test::DRC_ENGINE::AddMarker( MARKER_PCB* aMarker )
|
|
|
|
{
|
|
|
|
if( m_designSettings->Ignore( aMarker->GetRCItem()->GetErrorCode() ) )
|
|
|
|
{
|
|
|
|
delete aMarker;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_markers.push_back( aMarker );
|
|
|
|
}*/
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-13 23:28:08 +00:00
|
|
|
bool test::DRC_ENGINE::LoadRules( wxFileName aPath )
|
|
|
|
{
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-13 23:28:08 +00:00
|
|
|
if( aPath.FileExists() )
|
|
|
|
{
|
|
|
|
m_ruleConditions.clear();
|
|
|
|
m_rules.clear();
|
|
|
|
|
|
|
|
FILE* fp = wxFopen( aPath.GetFullPath(), wxT( "rt" ) );
|
|
|
|
|
|
|
|
if( fp )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
DRC_RULES_PARSER parser( m_board, fp, aPath.GetFullPath() );
|
2020-08-11 22:17:43 +00:00
|
|
|
parser.Parse( m_rules, m_reporter );
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
catch( PARSE_ERROR& pe )
|
|
|
|
{
|
|
|
|
// Don't leave possibly malformed stuff around for us to trip over
|
|
|
|
m_ruleConditions.clear();
|
|
|
|
m_rules.clear();
|
|
|
|
|
|
|
|
//wxSafeYield( m_editFrame );
|
|
|
|
//m_editFrame->ShowBoardSetupDialog( _( "Rules" ), pe.What(), ID_RULES_EDITOR,
|
2020-07-30 11:24:29 +00:00
|
|
|
// pe.lineNumber, pe.byteIndex );
|
2020-06-13 23:28:08 +00:00
|
|
|
|
|
|
|
throw;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
test::DRC_RULE* test::DRC_ENGINE::createInferredRule( const wxString& name, std::set<BOARD_ITEM*> items, int priority )
|
|
|
|
{
|
|
|
|
DRC_RULE *rule = new DRC_RULE;
|
|
|
|
|
|
|
|
rule->SetName( name );
|
|
|
|
if (! items.empty() )
|
|
|
|
rule->FillSpecificItemSet( items );
|
|
|
|
|
|
|
|
rule->SetPriority( priority );
|
|
|
|
|
|
|
|
addRule( rule );
|
|
|
|
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
int BOARD_CONNECTED_ITEM::GetClearance( PCB_LAYER_ID aLayer, BOARD_ITEM* aItem,
|
|
|
|
wxString* aSource ) const
|
|
|
|
{
|
|
|
|
BOARD* board = GetBoard();
|
|
|
|
int clearance = 0;
|
|
|
|
wxString source;
|
|
|
|
wxString* localSource = aSource ? &source : nullptr;
|
|
|
|
BOARD_CONNECTED_ITEM* second = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
|
|
|
|
|
|
|
|
// No clearance if "this" is not (yet) linked to a board therefore no available netclass
|
|
|
|
if( !board )
|
|
|
|
return clearance;
|
|
|
|
|
|
|
|
// LEVEL 1: local overrides (pad, footprint, etc.)
|
|
|
|
//
|
|
|
|
if( GetLocalClearanceOverrides() > clearance )
|
|
|
|
clearance = GetLocalClearanceOverrides( localSource );
|
|
|
|
|
|
|
|
if( second && second->GetLocalClearanceOverrides() > clearance )
|
|
|
|
clearance = second->GetLocalClearanceOverrides( localSource );
|
|
|
|
|
|
|
|
if( clearance )
|
|
|
|
{
|
|
|
|
if( aSource )
|
|
|
|
*aSource = *localSource;
|
|
|
|
|
|
|
|
return clearance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// LEVEL 2: Rules
|
|
|
|
//
|
|
|
|
if( GetRuleClearance( aItem, aLayer, &clearance, aSource ) )
|
|
|
|
return clearance;
|
|
|
|
|
|
|
|
// LEVEL 3: Accumulated local settings, netclass settings, & board design settings
|
|
|
|
//
|
|
|
|
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
|
|
|
NETCLASS* netclass = GetEffectiveNetclass();
|
|
|
|
NETCLASS* secondNetclass = second ? second->GetEffectiveNetclass() : nullptr;
|
|
|
|
|
|
|
|
if( bds.m_MinClearance > clearance )
|
|
|
|
{
|
|
|
|
if( aSource )
|
|
|
|
*aSource = _( "board minimum" );
|
|
|
|
|
|
|
|
clearance = bds.m_MinClearance;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( netclass && netclass->GetClearance() > clearance )
|
|
|
|
clearance = netclass->GetClearance( aSource );
|
|
|
|
|
|
|
|
if( secondNetclass && secondNetclass->GetClearance() > clearance )
|
|
|
|
clearance = secondNetclass->GetClearance( aSource );
|
|
|
|
|
|
|
|
if( aItem && aItem->GetLayer() == Edge_Cuts && bds.m_CopperEdgeClearance > clearance )
|
|
|
|
{
|
|
|
|
if( aSource )
|
|
|
|
*aSource = _( "board edge" );
|
|
|
|
|
|
|
|
clearance = bds.m_CopperEdgeClearance;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( GetLocalClearance() > clearance )
|
|
|
|
clearance = GetLocalClearance( aSource );
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
if( second && second->GetLocalClearance() > clearance )
|
|
|
|
clearance = second->GetLocalClearance( aSource );
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
return clearance;
|
|
|
|
}
|
|
|
|
#endif
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
|
|
|
|
void test::DRC_ENGINE::inferLegacyRules()
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-08-26 22:07:31 +00:00
|
|
|
int priorityRangeMin = INT_MIN + 10000;
|
|
|
|
int priorityRangeMax = INT_MAX - 10000;
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
ReportAux( wxString::Format( "Inferring implicit rules (per-item/class overrides, etc...)" ) );
|
2020-08-26 22:07:31 +00:00
|
|
|
|
|
|
|
// 1) global defaults
|
|
|
|
|
|
|
|
test::DRC_RULE* rule = createInferredRule( "inferred-defaults", {}, priorityRangeMin );
|
|
|
|
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
|
|
|
|
|
|
|
DRC_CONSTRAINT clearanceConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_CLEARANCE );
|
|
|
|
clearanceConstraint.Value().SetMin( bds.m_MinClearance );
|
|
|
|
rule->AddConstraint( clearanceConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT widthConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_TRACK_WIDTH );
|
|
|
|
widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
|
|
|
|
rule->AddConstraint( widthConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT drillConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_HOLE_SIZE );
|
|
|
|
drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
|
|
|
|
rule->AddConstraint( drillConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT annulusConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_ANNULUS_WIDTH );
|
|
|
|
annulusConstraint.Value().SetMin( bds.m_ViasMinAnnulus );
|
|
|
|
rule->AddConstraint( annulusConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT diameterConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_VIA_DIAMETER );
|
|
|
|
diameterConstraint.Value().SetMin( bds.m_ViasMinSize );
|
|
|
|
rule->AddConstraint( diameterConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT edgeClearanceConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE );
|
|
|
|
edgeClearanceConstraint.Value().SetMin( bds.m_CopperEdgeClearance );
|
|
|
|
rule->AddConstraint( edgeClearanceConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT holeClearanceConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE );
|
|
|
|
holeClearanceConstraint.Value().SetMin( bds.m_HoleToHoleMin );
|
|
|
|
rule->AddConstraint( holeClearanceConstraint );
|
|
|
|
|
2020-09-07 12:30:51 +00:00
|
|
|
DRC_CONSTRAINT courtyardClearanceConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_COURTYARD_CLEARANCE );
|
|
|
|
holeClearanceConstraint.Value().SetMin( 0 );
|
|
|
|
rule->AddConstraint( courtyardClearanceConstraint );
|
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
// 2) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
|
|
|
|
|
|
|
|
priorityRangeMin++;
|
|
|
|
|
|
|
|
auto isMicroViaCondition = new DRC_RULE_CONDITION ( "A.type == 'Via' && A.isMicroVia()" );
|
|
|
|
test::DRC_RULE* uViaRule = createInferredRule( "inferred-microvia-defaults", {}, priorityRangeMin );
|
|
|
|
|
|
|
|
uViaRule->SetCondition( isMicroViaCondition );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT uViaDrillConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_HOLE_SIZE );
|
|
|
|
uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
|
|
|
|
uViaRule->AddConstraint( uViaDrillConstraint );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT uViaDiameterConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_VIA_DIAMETER );
|
|
|
|
uViaDiameterConstraint.Value().SetMin( bds.m_MicroViasMinSize );
|
|
|
|
uViaRule->AddConstraint( uViaDiameterConstraint );
|
|
|
|
|
|
|
|
auto isBlindBuriedViaCondition = new DRC_RULE_CONDITION ( "A.type == 'Via' && A.isBlindBuriedVia()" );
|
|
|
|
test::DRC_RULE* blindBuriedViaRule = createInferredRule( "inferred-blind-buried-via-defaults", {}, priorityRangeMin );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT disallowConstraint( test::DRC_CONSTRAINT_TYPE_T::DRC_CONSTRAINT_TYPE_DISALLOW );
|
|
|
|
|
|
|
|
blindBuriedViaRule->SetCondition ( isBlindBuriedViaCondition );
|
|
|
|
|
|
|
|
if( !bds.m_MicroViasAllowed )
|
|
|
|
{
|
|
|
|
uViaRule->AddConstraint( disallowConstraint );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !bds.m_BlindBuriedViaAllowed )
|
|
|
|
{
|
|
|
|
blindBuriedViaRule->AddConstraint( disallowConstraint );
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3) per-netclass rules
|
|
|
|
|
|
|
|
struct NETCLASS_ENTRY
|
|
|
|
{
|
|
|
|
wxString name;
|
|
|
|
int clearance;
|
|
|
|
int width;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<NETCLASS_ENTRY> netclassesByClearance, netclassesByWidth;
|
|
|
|
|
2020-09-04 23:19:01 +00:00
|
|
|
m_board->SynchronizeNetsAndNetClasses();
|
|
|
|
|
|
|
|
// fixme: make this conditional for standalone tests
|
|
|
|
bds.SetNetClasses( nullptr ); // load legacy
|
|
|
|
|
|
|
|
std::vector<NETCLASSPTR> netclasses;
|
|
|
|
|
|
|
|
netclasses.push_back( bds.GetNetClasses().GetDefault() );
|
|
|
|
|
|
|
|
for( auto netclass : bds.GetNetClasses() )
|
|
|
|
netclasses.push_back( netclass.second );
|
|
|
|
|
|
|
|
ReportAux( wxString::Format( "Importing %d legacy net classes", (int) netclasses.size() ) );
|
|
|
|
|
|
|
|
int i = 0;
|
2020-09-07 12:30:51 +00:00
|
|
|
|
2020-09-04 23:19:01 +00:00
|
|
|
for( auto &nc : netclasses )
|
|
|
|
{
|
|
|
|
wxString className = nc->GetName();
|
2020-08-26 22:07:31 +00:00
|
|
|
|
|
|
|
const auto expr = wxString::Format( "A.NetClass == '%s' || B.NetClass == '%s'", className, className );
|
|
|
|
|
|
|
|
auto inNetclassCondition = new DRC_RULE_CONDITION ( expr );
|
|
|
|
|
|
|
|
test::DRC_RULE* netclassRule = createInferredRule( wxString::Format( "inferred-netclass-clearance-%s", className ),
|
|
|
|
{}, priorityRangeMin + i );
|
|
|
|
|
|
|
|
netclassRule->SetCondition( inNetclassCondition );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT ncClearanceConstraint ( DRC_CONSTRAINT_TYPE_CLEARANCE );
|
2020-09-04 23:19:01 +00:00
|
|
|
ncClearanceConstraint.Value().SetMin( nc->GetClearance() );
|
2020-08-26 22:07:31 +00:00
|
|
|
netclassRule->AddConstraint( ncClearanceConstraint );
|
|
|
|
|
|
|
|
netclassRule = createInferredRule( wxString::Format( "inferred-netclass-width-%s", className ),
|
|
|
|
{}, priorityRangeMin + i );
|
|
|
|
|
|
|
|
netclassRule->SetCondition( inNetclassCondition );
|
|
|
|
|
|
|
|
DRC_CONSTRAINT ncWidthConstraint ( DRC_CONSTRAINT_TYPE_TRACK_WIDTH );
|
2020-09-04 23:19:01 +00:00
|
|
|
ncWidthConstraint.Value().SetMin( nc->GetTrackWidth() );
|
2020-08-26 22:07:31 +00:00
|
|
|
netclassRule->AddConstraint( ncWidthConstraint );
|
|
|
|
|
|
|
|
// TODO: should we import diff pair gaps/widths here?
|
2020-09-04 23:19:01 +00:00
|
|
|
i++;
|
2020-08-26 22:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-09-04 23:19:01 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
//clearanceConstraint.SetMin( )
|
|
|
|
|
|
|
|
//rule->AddConstraint( )
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static wxString formatConstraint( const test::DRC_CONSTRAINT& constraint )
|
|
|
|
{
|
|
|
|
struct Formatter
|
|
|
|
{
|
|
|
|
test::DRC_CONSTRAINT_TYPE_T type;
|
|
|
|
wxString name;
|
|
|
|
std::function<wxString(const test::DRC_CONSTRAINT&)> formatter;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto formatMinMax = []( const test::DRC_CONSTRAINT& c ) -> wxString {
|
|
|
|
wxString str;
|
|
|
|
const auto value = c.GetValue();
|
|
|
|
|
|
|
|
if ( value.HasMin() )
|
|
|
|
str += wxString::Format(" min: %d", value.Min() );
|
|
|
|
if ( value.HasOpt() )
|
|
|
|
str += wxString::Format(" opt: %d", value.Opt() );
|
|
|
|
if ( value.HasMax() )
|
|
|
|
str += wxString::Format(" max: %d", value.Max() );
|
|
|
|
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<Formatter> formats = {
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_UNKNOWN, "unknown", nullptr },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_CLEARANCE, "clearance", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE, "hole_clearance", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE, "edge_clearance", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_HOLE_SIZE, "hole_size", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_COURTYARD_CLEARANCE, "courtyard_clearance", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_SILK_TO_PAD, "silk_to_pad", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_SILK_TO_SILK, "silk_to_silk", formatMinMax },
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_TRACK_WIDTH, "track_width", formatMinMax },
|
2020-09-07 12:30:51 +00:00
|
|
|
{ test::DRC_CONSTRAINT_TYPE_ANNULUS_WIDTH, "annulus_width", formatMinMax },
|
2020-08-26 22:07:31 +00:00
|
|
|
{ test::DRC_CONSTRAINT_TYPE_DISALLOW, "disallow", nullptr }, // fixme
|
|
|
|
{ test::DRC_CONSTRAINT_TYPE_VIA_DIAMETER, "via_diameter", formatMinMax }
|
|
|
|
};
|
|
|
|
|
|
|
|
for( auto& fmt : formats)
|
|
|
|
{
|
|
|
|
if( fmt.type == constraint.GetType() )
|
|
|
|
{
|
|
|
|
wxString rv = fmt.name + " ";
|
|
|
|
if( fmt.formatter )
|
|
|
|
rv += fmt.formatter( constraint );
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "?";
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool test::DRC_ENGINE::CompileRules()
|
|
|
|
{
|
2020-08-26 22:07:31 +00:00
|
|
|
ReportAux( wxString::Format( "Compiling Rules (%d rules, %d conditions): ", (int)m_rules.size(), (int)m_ruleConditions.size() ) );
|
2020-06-13 23:28:08 +00:00
|
|
|
|
|
|
|
for( auto provider : m_testProviders )
|
|
|
|
{
|
2020-06-18 16:55:22 +00:00
|
|
|
ReportAux( wxString::Format( "- Provider: '%s': ", provider->GetName() ) );
|
2020-08-25 17:42:52 +00:00
|
|
|
drc_dbg(7, "do prov %s", provider->GetName() );
|
2020-06-18 16:55:22 +00:00
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
for ( auto id : provider->GetMatchingConstraintIds() )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-08-25 17:42:52 +00:00
|
|
|
drc_dbg(7, "do id %d", id);
|
2020-08-12 22:19:34 +00:00
|
|
|
if( m_constraintMap.find(id) == m_constraintMap.end() )
|
|
|
|
m_constraintMap[id] = new CONSTRAINT_SET;
|
2020-08-11 22:17:43 +00:00
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
m_constraintMap[ id ]->provider = provider;
|
2020-06-13 23:28:08 +00:00
|
|
|
|
|
|
|
for( auto rule : m_rules )
|
2020-08-11 22:17:43 +00:00
|
|
|
{
|
2020-08-26 22:07:31 +00:00
|
|
|
test::DRC_RULE_CONDITION* condition = nullptr;
|
|
|
|
bool compileOk = false;
|
|
|
|
std::vector<test::DRC_CONSTRAINT> matchingConstraints;
|
2020-08-25 17:42:52 +00:00
|
|
|
drc_dbg(7, "Scan provider %s, rule %s", provider->GetName(), rule->GetName() );
|
2020-08-11 22:17:43 +00:00
|
|
|
|
|
|
|
if( ! rule->IsEnabled() )
|
|
|
|
continue;
|
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
|
|
|
|
if( rule->IsConditional() )
|
|
|
|
{
|
|
|
|
condition = rule->Condition();
|
|
|
|
compileOk = condition->Compile( nullptr, 0, 0 ); // fixme
|
|
|
|
}
|
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
for( auto& constraint : rule->Constraints() )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-08-25 17:42:52 +00:00
|
|
|
drc_dbg(7, "scan constraint id %d\n", constraint.GetType() );
|
2020-08-11 22:17:43 +00:00
|
|
|
if( constraint.GetType() != id )
|
|
|
|
continue;
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
auto rcons = new CONSTRAINT_WITH_CONDITIONS;
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
if( condition )
|
2020-06-17 22:36:54 +00:00
|
|
|
{
|
2020-08-11 22:17:43 +00:00
|
|
|
rcons->conditions.push_back( condition );
|
2020-06-17 22:36:54 +00:00
|
|
|
}
|
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
matchingConstraints.push_back( constraint );
|
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
rcons->constraint = constraint;
|
|
|
|
rcons->parentRule = rule;
|
|
|
|
m_constraintMap[ id ]->sortedConstraints.push_back( rcons );
|
2020-08-11 22:17:43 +00:00
|
|
|
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
2020-08-26 22:07:31 +00:00
|
|
|
|
|
|
|
if( !matchingConstraints.empty() )
|
|
|
|
{
|
|
|
|
ReportAux( wxString::Format( " |- Rule: '%s' ", rule->GetName() ) );
|
|
|
|
if( condition )
|
|
|
|
ReportAux( wxString::Format( " |- condition: '%s' compile: %s", condition->GetExpression(), compileOk ? "OK" : "ERROR") );
|
|
|
|
|
|
|
|
for (const auto& constraint : matchingConstraints )
|
|
|
|
{
|
|
|
|
ReportAux( wxString::Format( " |- constraint: %s", formatConstraint( constraint ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void test::DRC_ENGINE::RunTests( )
|
|
|
|
{
|
2020-08-11 22:15:50 +00:00
|
|
|
m_drcReport.reset( new test::DRC_REPORT );
|
2020-06-17 22:36:54 +00:00
|
|
|
m_testProviders = DRC_TEST_PROVIDER_REGISTRY::Instance().GetTestProviders();
|
|
|
|
|
|
|
|
for( auto provider : m_testProviders )
|
2020-06-18 16:55:22 +00:00
|
|
|
{
|
|
|
|
ReportAux( wxString::Format( "Create DRC provider: '%s'", provider->GetName() ) );
|
2020-06-17 22:36:54 +00:00
|
|
|
provider->SetDRCEngine( this );
|
2020-06-18 16:55:22 +00:00
|
|
|
}
|
|
|
|
|
2020-06-17 22:36:54 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
inferLegacyRules();
|
2020-06-13 23:28:08 +00:00
|
|
|
CompileRules();
|
2020-07-23 14:22:45 +00:00
|
|
|
|
2020-06-17 22:36:54 +00:00
|
|
|
for( auto provider : m_testProviders )
|
|
|
|
{
|
2020-07-23 14:22:45 +00:00
|
|
|
bool skipProvider = false;
|
2020-08-25 17:42:52 +00:00
|
|
|
auto matchingConstraints = provider->GetMatchingConstraintIds();
|
2020-07-23 14:22:45 +00:00
|
|
|
|
2020-08-25 17:42:52 +00:00
|
|
|
if( matchingConstraints.size() )
|
2020-07-23 14:22:45 +00:00
|
|
|
{
|
2020-08-25 17:42:52 +00:00
|
|
|
for( auto ruleID : matchingConstraints )
|
2020-07-23 14:22:45 +00:00
|
|
|
{
|
2020-08-25 17:42:52 +00:00
|
|
|
if( !HasCorrectRulesForId( ruleID ) )
|
|
|
|
{
|
|
|
|
ReportAux( wxString::Format( "DRC provider '%s' has no rules provided. Skipping run.", provider->GetName() ) );
|
|
|
|
skipProvider = true;
|
|
|
|
break;
|
|
|
|
}
|
2020-07-23 14:22:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( skipProvider )
|
|
|
|
continue;
|
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
drc_dbg(0, "Running test provider: '%s'\n", provider->GetName() );
|
2020-06-18 16:55:22 +00:00
|
|
|
ReportAux( wxString::Format( "Run DRC provider: '%s'", provider->GetName() ) );
|
2020-06-17 22:36:54 +00:00
|
|
|
provider->Run();
|
|
|
|
}
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
const test::DRC_CONSTRAINT& test::DRC_ENGINE::EvalRulesForItems( test::DRC_CONSTRAINT_TYPE_T aConstraintId, BOARD_ITEM* a, BOARD_ITEM* b, PCB_LAYER_ID aLayer )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-06-17 22:36:54 +00:00
|
|
|
test::DRC_RULE* rv;
|
2020-08-12 22:19:34 +00:00
|
|
|
auto ruleset = m_constraintMap[ aConstraintId ];
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
for( auto rcond : ruleset->sortedConstraints )
|
2020-06-17 22:36:54 +00:00
|
|
|
{
|
2020-08-12 22:19:34 +00:00
|
|
|
if( rcond->conditions.size() == 0 ) // uconditional
|
|
|
|
{
|
|
|
|
drc_dbg( 8, " -> rule '%s' matches (unconditional)\n",
|
2020-08-18 14:17:16 +00:00
|
|
|
rcond->constraint.GetParentRule()->GetName()
|
2020-08-12 22:19:34 +00:00
|
|
|
);
|
|
|
|
return rcond->constraint;
|
|
|
|
}
|
2020-06-17 22:36:54 +00:00
|
|
|
for( auto condition : rcond->conditions )
|
|
|
|
{
|
2020-07-31 17:37:24 +00:00
|
|
|
drc_dbg( 8, " -> check condition '%s'\n",
|
2020-08-11 22:17:43 +00:00
|
|
|
condition->GetExpression() );
|
2020-07-27 13:33:06 +00:00
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
bool result = condition->EvaluateFor( a, b, aLayer ); // FIXME: need the actual layer
|
2020-06-17 22:36:54 +00:00
|
|
|
if( result )
|
|
|
|
{
|
2020-07-31 17:37:24 +00:00
|
|
|
drc_dbg( 8, " -> rule '%s' matches, triggered by condition '%s'\n",
|
2020-08-12 22:19:34 +00:00
|
|
|
rcond->constraint.GetParentRule()->GetName(),
|
2020-08-11 22:17:43 +00:00
|
|
|
condition->GetExpression() );
|
2020-08-12 22:19:34 +00:00
|
|
|
|
|
|
|
return rcond->constraint;
|
2020-06-17 22:36:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 23:11:06 +00:00
|
|
|
// fixme: return optional<drc_constraint>, let the particular test decide what to do if no matching constraint
|
|
|
|
// is found
|
|
|
|
static test::DRC_CONSTRAINT nullConstraint;
|
|
|
|
nullConstraint.m_DisallowFlags = 0;
|
|
|
|
nullConstraint.m_LayerCondition.reset();
|
|
|
|
|
|
|
|
return nullConstraint;
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-11 22:15:50 +00:00
|
|
|
void test::DRC_ENGINE::Report( std::shared_ptr<DRC_ITEM> aItem, ::MARKER_PCB *aMarker )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-08-11 22:15:50 +00:00
|
|
|
m_drcReport->AddItem( aItem, aMarker );
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
if( m_reporter )
|
|
|
|
{
|
2020-08-25 17:42:52 +00:00
|
|
|
wxString msg = wxString::Format( "Test '%s': %s (code %d)",
|
|
|
|
aItem->GetViolatingTest()->GetName(),
|
|
|
|
aItem->GetErrorMessage(),
|
|
|
|
aItem->GetErrorCode() );
|
|
|
|
|
|
|
|
auto rule = aItem->GetViolatingRule();
|
|
|
|
|
|
|
|
if( rule )
|
|
|
|
msg += wxString::Format( ", violating rule: '%s'", rule->GetName() );
|
|
|
|
|
|
|
|
m_reporter->Report ( msg, RPT_SEVERITY_ERROR /* fixme */ );
|
|
|
|
|
2020-08-11 22:15:50 +00:00
|
|
|
wxString violatingItemsStr = "Violating items: ";
|
|
|
|
|
|
|
|
if( aMarker )
|
|
|
|
{
|
|
|
|
m_reporter->Report( wxString::Format( " |- violating position (%d, %d)", aMarker->GetPos().x, aMarker->GetPos().y ),
|
|
|
|
RPT_SEVERITY_ERROR /* fixme */ );
|
|
|
|
}
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
void test::DRC_ENGINE::ReportAux ( const wxString& aStr )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-06-18 16:55:22 +00:00
|
|
|
if( !m_reporter )
|
2020-06-13 23:28:08 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
m_reporter->Report( aStr, RPT_SEVERITY_INFO );
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
void test::DRC_ENGINE::ReportProgress( double aProgress )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-06-18 16:55:22 +00:00
|
|
|
if( !m_progressReporter )
|
2020-06-13 23:28:08 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
m_progressReporter->SetCurrentProgress( aProgress );
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-18 16:55:22 +00:00
|
|
|
void test::DRC_ENGINE::ReportStage ( const wxString& aStageName, int index, int total )
|
2020-06-13 23:28:08 +00:00
|
|
|
{
|
2020-06-18 16:55:22 +00:00
|
|
|
if( !m_progressReporter )
|
|
|
|
return;
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-08-26 22:07:31 +00:00
|
|
|
m_progressReporter->SetNumPhases( total );
|
2020-08-18 14:17:16 +00:00
|
|
|
m_progressReporter->BeginPhase( index ); // fixme: coalesce all stages/test providers
|
2020-08-26 22:07:31 +00:00
|
|
|
m_progressReporter->Report( aStageName );
|
2020-06-13 23:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-06-30 08:44:12 +00:00
|
|
|
#if 0
|
2020-08-11 22:17:43 +00:00
|
|
|
test::DRC_CONSTRAINT test::DRC_ENGINE::GetWorstGlobalConstraint( test::DRC_CONSTRAINT_TYPE_T ruleID )
|
2020-06-30 08:44:12 +00:00
|
|
|
{
|
|
|
|
DRC_CONSTRAINT rv;
|
|
|
|
|
|
|
|
rv.m_Value.SetMin( std::numeric_limits<int>::max() );
|
|
|
|
rv.m_Value.SetMax( std::numeric_limits<int>::min() );
|
|
|
|
for( auto rule : QueryRulesById( ruleID ) )
|
|
|
|
{
|
|
|
|
auto mm = rule->GetConstraint().m_Value;
|
|
|
|
if( mm.HasMax() )
|
|
|
|
rv.m_Value.SetMax( std::max( mm.Max(), rv.m_Value.Max() ) );
|
|
|
|
if( mm.HasMin() )
|
|
|
|
rv.m_Value.SetMin( std::min( mm.Min(), rv.m_Value.Min() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-08-18 14:17:16 +00:00
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
std::vector<test::DRC_CONSTRAINT> test::DRC_ENGINE::QueryConstraintsById( test::DRC_CONSTRAINT_TYPE_T constraintID )
|
2020-06-30 08:44:12 +00:00
|
|
|
{
|
2020-08-11 22:17:43 +00:00
|
|
|
std::vector<test::DRC_CONSTRAINT> rv;
|
2020-08-12 22:19:34 +00:00
|
|
|
for ( auto c : m_constraintMap[constraintID]->sortedConstraints )
|
|
|
|
rv.push_back(c->constraint);
|
2020-06-30 08:44:12 +00:00
|
|
|
return rv;
|
|
|
|
}
|
2020-06-13 23:28:08 +00:00
|
|
|
|
2020-07-23 14:22:45 +00:00
|
|
|
|
2020-08-12 22:19:34 +00:00
|
|
|
bool test::DRC_ENGINE::HasCorrectRulesForId( test::DRC_CONSTRAINT_TYPE_T constraintID )
|
2020-07-23 14:22:45 +00:00
|
|
|
{
|
2020-08-12 22:19:34 +00:00
|
|
|
//drc_dbg(10,"hascorrect id %d size %d\n", ruleID, m_ruleMap[ruleID]->sortedRules.size( ) );
|
|
|
|
return m_constraintMap[constraintID]->sortedConstraints.size() != 0;
|
2020-07-23 14:22:45 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 22:15:50 +00:00
|
|
|
|
2020-08-11 22:17:43 +00:00
|
|
|
bool test::DRC_ENGINE::QueryWorstConstraint( test::DRC_CONSTRAINT_TYPE_T aConstraintId, test::DRC_CONSTRAINT& aConstraint, test::DRC_CONSTRAINT_QUERY_T aQueryType )
|
2020-08-11 22:15:50 +00:00
|
|
|
{
|
|
|
|
if( aQueryType == DRCCQ_LARGEST_MINIMUM )
|
|
|
|
{
|
|
|
|
int worst = 0;
|
2020-08-11 22:17:43 +00:00
|
|
|
for( const auto constraint : QueryConstraintsById( aConstraintId ) )
|
2020-08-11 22:15:50 +00:00
|
|
|
{
|
2020-08-11 22:17:43 +00:00
|
|
|
if( constraint.GetValue().HasMin() )
|
2020-08-11 22:15:50 +00:00
|
|
|
{
|
2020-08-11 22:17:43 +00:00
|
|
|
int current = constraint.GetValue().Min();
|
2020-08-11 22:15:50 +00:00
|
|
|
|
|
|
|
if( current > worst )
|
|
|
|
{
|
|
|
|
worst = current;
|
2020-08-11 22:17:43 +00:00
|
|
|
aConstraint = constraint;
|
2020-08-11 22:15:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return worst > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|