/* * 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 */ // fixme - way too much includes #include #include #include #include #include #include #include #include #include test::DRC_ENGINE::DRC_ENGINE( BOARD* aBoard, BOARD_DESIGN_SETTINGS *aSettings ) : m_board( aBoard ), m_designSettings ( aSettings ), m_reporter( nullptr ), m_progressReporter( nullptr ) { } test::DRC_ENGINE::~DRC_ENGINE() { for( DRC_ITEM* item : m_drcItems ) delete item; } /*void test::DRC_ENGINE::AddMarker( MARKER_PCB* aMarker ) { if( m_designSettings->Ignore( aMarker->GetRCItem()->GetErrorCode() ) ) { delete aMarker; return; } m_markers.push_back( aMarker ); }*/ bool test::DRC_ENGINE::LoadRules( wxFileName aPath ) { 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() ); parser.Parse( m_ruleConditions, m_rules, &NULL_REPORTER::GetInstance() ); } 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, // pe.lineNumber, pe.byteIndex ); throw; return false; } } } return true; } test::DRC_TEST_PROVIDER *drcCreateClearanceTestProvider( test::DRC_ENGINE *engine ); void test::DRC_ENGINE::inferImplicitRules() { ReportAux( wxString::Format( "Inferring implicit rules (per-item/class overrides, etc...)" ) ); } static const int drc_debug_level = 0; void test::drc_dbg( int level, const char* fmt, ... ) { #ifdef DEBUG long dlevel = drc_debug_level; wxString dlevelstr; if( wxGetEnv( "DRC_DEBUG_LEVEL", &dlevelstr ) ) { dlevelstr.ToLong( &dlevel ); } if(level < dlevel) // fixme: tom's debugging. { va_list ap; va_start( ap, fmt ); fprintf( stderr, "drc: " ); vfprintf( stderr, fmt, ap ); va_end( ap ); } #endif } bool test::DRC_ENGINE::CompileRules() { ReportAux( wxString::Format( "Compiling Rules (%d rules, %d conditions): ", m_rules.size(), m_ruleConditions.size() ) ); for( auto provider : m_testProviders ) { ReportAux( wxString::Format( "- Provider: '%s': ", provider->GetName() ) ); for ( auto id : provider->GetMatchingRuleIds() ) { if( m_ruleMap.find(id) == m_ruleMap.end() ) m_ruleMap[id] = new RULE_SET; m_ruleMap[ id ]->provider = provider; m_ruleMap[ id ]->defaultRule = nullptr; for( auto rule : m_rules ) { if( rule->GetTestProviderName() == provider->GetName() ) { ReportAux( wxString::Format( " |- Rule: '%s' ", rule->m_Name.c_str() ) ); if( rule->IsEnabled() ) { auto rcons = new RULE_WITH_CONDITIONS; if( rule->GetPriority() == 0 ) { drc_dbg(1,"DefaultRule for %d = %p\n", id, rule ); m_ruleMap[ id ]->defaultRule = rule; continue; } for( auto condition : m_ruleConditions ) { if( condition->m_TargetRuleName == rule->GetName() ) { rcons->conditions.push_back( condition ); bool compileOk = condition->Compile( &NULL_REPORTER::GetInstance() ); ReportAux( wxString::Format( " |- condition: '%s' compile: %s", condition->m_TargetRuleName, compileOk ? "OK" : "ERROR") ); rcons->rule = rule; m_ruleMap[ id ]->sortedRules.push_back( rcons ); } } } } } } } return true; } void test::DRC_ENGINE::RunTests( ) { //m_largestClearance = m_designSettings->GetBiggestClearanceValue(); m_testProviders = DRC_TEST_PROVIDER_REGISTRY::Instance().GetTestProviders(); for( auto provider : m_testProviders ) { ReportAux( wxString::Format( "Create DRC provider: '%s'", provider->GetName() ) ); provider->SetDRCEngine( this ); } inferImplicitRules(); CompileRules(); for( auto provider : m_testProviders ) { bool skipProvider = false; for( auto ruleID : provider->GetMatchingRuleIds() ) { if( !HasCorrectRulesForId( ruleID ) ) { ReportAux( wxString::Format( "DRC provider '%s' has no rules provided. Skipping run.", provider->GetName() ) ); skipProvider = true; break; } } if( skipProvider ) continue; drc_dbg(0, "Running test provider: '%s'\n", (const char *) provider->GetName().c_str() ); ReportAux( wxString::Format( "Run DRC provider: '%s'", provider->GetName() ) ); provider->Run(); } } test::DRC_RULE* test::DRC_ENGINE::EvalRulesForItems( test::DRC_RULE_ID_T ruleID, BOARD_ITEM* a, BOARD_ITEM* b ) { test::DRC_RULE* rv; auto ruleset = m_ruleMap[ ruleID ]; for( auto rcond : ruleset->sortedRules ) { for( auto condition : rcond->conditions ) { drc_dbg( 8, " -> check condition '%s'\n", (const char*) condition->m_Expression.c_str() ); bool result = condition->EvaluateFor( a, b ); if( result ) { drc_dbg( 8, " -> rule '%s' matches, triggered by condition '%s'\n", (const char*) rcond->rule->m_Name.c_str(), (const char*) condition->m_Expression.c_str() ); return rcond->rule; } } } if( ruleset->defaultRule ) { drc_dbg(8, " -> default rule '%s' matches\n", (const char*) ruleset->defaultRule->m_Name.c_str() ); return ruleset->defaultRule; } assert(false); // should never hapen return nullptr; } void test::DRC_ENGINE::Report( DRC_ITEM* aItem, ::MARKER_PCB *aMarker ) { m_drcItems.push_back( aItem ); m_markers.push_back( aMarker ); if( m_reporter ) { m_reporter->Report ( wxString::Format( "Test '%s': violation of rule '%s' : %s (code %d)", aItem->GetViolatingTest()->GetName(), aItem->GetViolatingRule()->GetName(), aItem->GetErrorMessage(), aItem->GetErrorCode() ), RPT_SEVERITY_ERROR /* fixme */ ); if( aMarker ) { m_reporter->Report( wxString::Format( " |- violating position (%d, %d)", aMarker->GetPos().x, aMarker->GetPos().y ), RPT_SEVERITY_ERROR /* fixme */ ); } } } void test::DRC_ENGINE::ReportAux ( const wxString& aStr ) { if( !m_reporter ) return; m_reporter->Report( aStr, RPT_SEVERITY_INFO ); } void test::DRC_ENGINE::ReportProgress( double aProgress ) { if( !m_progressReporter ) return; m_progressReporter->SetCurrentProgress( aProgress ); } void test::DRC_ENGINE::ReportStage ( const wxString& aStageName, int index, int total ) { if( !m_progressReporter ) return; m_progressReporter->BeginPhase( index ); // fixme: coalesce all stages/test providers } #if 0 test::DRC_CONSTRAINT test::DRC_ENGINE::GetWorstGlobalConstraint( test::DRC_RULE_ID_T ruleID ) { DRC_CONSTRAINT rv; rv.m_Value.SetMin( std::numeric_limits::max() ); rv.m_Value.SetMax( std::numeric_limits::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 std::vector test::DRC_ENGINE::QueryRulesById( test::DRC_RULE_ID_T ruleID ) { std::vector rv; auto dr = m_ruleMap[ruleID]->defaultRule; assert( dr ); rv.push_back( dr ); for( auto rule : m_ruleMap[ruleID]->sortedRules ) { assert( rule ); assert( rule->rule ); rv.push_back(rule->rule); } return rv; } bool test::DRC_ENGINE::HasCorrectRulesForId( test::DRC_RULE_ID_T ruleID ) { return m_ruleMap[ruleID]->defaultRule != nullptr; }