Move FP type check to FP DRC

Checks for validity are better centrally located where they don't
interrupt user actions
This commit is contained in:
Seth Hillbrand 2021-09-25 08:55:33 -07:00
parent 46949abe4a
commit 3e7310a219
6 changed files with 70 additions and 34 deletions

View File

@ -51,6 +51,7 @@ DIALOG_FOOTPRINT_CHECKER::DIALOG_FOOTPRINT_CHECKER( FOOTPRINT_EDIT_FRAME* aParen
m_sdbSizerCancel->SetLabel( _( "Close" ) ); m_sdbSizerCancel->SetLabel( _( "Close" ) );
m_sdbSizerOK->SetDefault(); m_sdbSizerOK->SetDefault();
m_sdbSizer->Layout();
syncCheckboxes(); syncCheckboxes();
@ -66,8 +67,6 @@ DIALOG_FOOTPRINT_CHECKER::~DIALOG_FOOTPRINT_CHECKER()
bool DIALOG_FOOTPRINT_CHECKER::TransferDataToWindow() bool DIALOG_FOOTPRINT_CHECKER::TransferDataToWindow()
{ {
runChecks();
return true; return true;
} }
@ -120,6 +119,21 @@ void DIALOG_FOOTPRINT_CHECKER::runChecks()
footprint->BuildPolyCourtyards( &errorHandler ); footprint->BuildPolyCourtyards( &errorHandler );
const std::function<void( const wxString& msg )> typeWarning =
[&]( const wxString& aMsg )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_FOOTPRINT_TYPE_MISMATCH );
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + aMsg );
drcItem->SetItems( footprint );
PCB_MARKER* marker = new PCB_MARKER( drcItem, wxPoint( 0, 0 ) );
board->Add( marker );
m_frame->GetCanvas()->GetView()->Add( marker );
};
footprint->CheckFootprintAttributes( &typeWarning );
m_checksRun = true; m_checksRun = true;
SetMarkersProvider( new BOARD_DRC_ITEMS_PROVIDER( m_frame->GetBoard() ) ); SetMarkersProvider( new BOARD_DRC_ITEMS_PROVIDER( m_frame->GetBoard() ) );

View File

@ -202,6 +202,10 @@ DRC_ITEM DRC_ITEM::diffPairUncoupledLengthTooLong( DRCE_DIFF_PAIR_UNCOUPLED_LENG
_( "Differential uncoupled length too long" ), _( "Differential uncoupled length too long" ),
wxT( "diff_pair_uncoupled_length_too_long" ) ); wxT( "diff_pair_uncoupled_length_too_long" ) );
DRC_ITEM DRC_ITEM::footprintTypeMismatch( DRCE_FOOTPRINT_TYPE_MISMATCH,
_( "Footprint type doesn't match footprint pads" ),
wxT( "footprint_type_mismatch" ) );
std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( { std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
DRC_ITEM::heading_electrical, DRC_ITEM::heading_electrical,
DRC_ITEM::shortingItems, DRC_ITEM::shortingItems,
@ -248,6 +252,8 @@ std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
DRC_ITEM::npthInsideCourtyard, DRC_ITEM::npthInsideCourtyard,
DRC_ITEM::itemOnDisabledLayer, DRC_ITEM::itemOnDisabledLayer,
DRC_ITEM::unresolvedVariable, DRC_ITEM::unresolvedVariable,
DRC_ITEM::footprintTypeMismatch
} ); } );
@ -294,6 +300,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
case DRCE_TOO_MANY_VIAS: return std::make_shared<DRC_ITEM>( tooManyVias ); case DRCE_TOO_MANY_VIAS: return std::make_shared<DRC_ITEM>( tooManyVias );
case DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE: return std::make_shared<DRC_ITEM>( diffPairGapOutOfRange ); case DRCE_DIFF_PAIR_GAP_OUT_OF_RANGE: return std::make_shared<DRC_ITEM>( diffPairGapOutOfRange );
case DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG: return std::make_shared<DRC_ITEM>( diffPairUncoupledLengthTooLong ); case DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG: return std::make_shared<DRC_ITEM>( diffPairUncoupledLengthTooLong );
case DRCE_FOOTPRINT_TYPE_MISMATCH: return std::make_shared<DRC_ITEM>( footprintTypeMismatch );
default: default:
wxFAIL_MSG( "Unknown DRC error code" ); wxFAIL_MSG( "Unknown DRC error code" );
return nullptr; return nullptr;

View File

@ -67,6 +67,8 @@ enum PCB_DRC_CODE {
DRCE_EXTRA_FOOTPRINT, // netlist item not found for footprint DRCE_EXTRA_FOOTPRINT, // netlist item not found for footprint
DRCE_NET_CONFLICT, // pad net doesn't match netlist DRCE_NET_CONFLICT, // pad net doesn't match netlist
DRCE_FOOTPRINT_TYPE_MISMATCH, // footprint attribute does not match actual pads
DRCE_UNRESOLVED_VARIABLE, DRCE_UNRESOLVED_VARIABLE,
DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it
// over pads, exposed copper, etc.) // over pads, exposed copper, etc.)
@ -169,6 +171,7 @@ private:
static DRC_ITEM tooManyVias; static DRC_ITEM tooManyVias;
static DRC_ITEM diffPairGapOutOfRange; static DRC_ITEM diffPairGapOutOfRange;
static DRC_ITEM diffPairUncoupledLengthTooLong; static DRC_ITEM diffPairUncoupledLengthTooLong;
static DRC_ITEM footprintTypeMismatch;
private: private:
DRC_RULE* m_violatingRule = nullptr; DRC_RULE* m_violatingRule = nullptr;

View File

@ -2072,6 +2072,38 @@ void FOOTPRINT::BuildPolyCourtyards( OUTLINE_ERROR_HANDLER* aErrorHandler )
} }
void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& msg )>* aErrorHandler )
{
int likelyAttr = GetLikelyAttribute();
int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
// This is only valid if the footprint doesn't have FP_SMD and FP_THROUGH_HOLE set
// Which is, unfortunately, possible in theory but not in the UI (I think)
if( aErrorHandler && likelyAttr != setAttr )
{
wxString msg;
if( likelyAttr == FP_THROUGH_HOLE )
{
msg.Printf( _( "Expected \"Through hole\" type but set to \"%s\"" ), GetTypeName() );
}
else if( likelyAttr == FP_SMD )
{
msg.Printf( _( "Expected \"SMD\" type but set to \"%s\"" ), GetTypeName() );
}
else
{
msg.Printf( _( "Expected \"Other\" type but set to \"%s\"" ), GetTypeName() );
}
msg = "(" + msg + ")";
(*aErrorHandler)( msg );
}
}
void FOOTPRINT::SwapData( BOARD_ITEM* aImage ) void FOOTPRINT::SwapData( BOARD_ITEM* aImage )
{ {
wxASSERT( aImage->Type() == PCB_FOOTPRINT_T ); wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );

View File

@ -342,6 +342,18 @@ public:
void SetLastEditTime() { m_lastEditTime = time( nullptr ); } void SetLastEditTime() { m_lastEditTime = time( nullptr ); }
timestamp_t GetLastEditTime() const { return m_lastEditTime; } timestamp_t GetLastEditTime() const { return m_lastEditTime; }
/**
* Test if footprint attributes for type (SMD/Through hole/Other) match the expected
* type based on the pads in the footprint.
* Footprints with plated through-hole pads should usually be marked through hole even if they also
* have SMD because they might not be auto-placed. Exceptions to this might be shielded connectors
* Otherwise, footprints with SMD pads should be marked SMD
* Footprints with no connecting pads should be marked "Other"
*
* @param aErrorHandler callback to handle the error messages generated
*/
void CheckFootprintAttributes( const std::function<void( const wxString& msg )>* aErrorHandler );
/** /**
* Generate pads shapes on layer \a aLayer as polygons and adds these polygons to * Generate pads shapes on layer \a aLayer as polygons and adds these polygons to
* \a aCornerBuffer. * \a aCornerBuffer.

View File

@ -784,38 +784,6 @@ bool FOOTPRINT_EDIT_FRAME::SaveFootprint( FOOTPRINT* aFootprint )
wxString libraryName = aFootprint->GetFPID().GetLibNickname(); wxString libraryName = aFootprint->GetFPID().GetLibNickname();
wxString footprintName = aFootprint->GetFPID().GetLibItemName(); wxString footprintName = aFootprint->GetFPID().GetLibItemName();
bool nameChanged = m_footprintNameWhenLoaded != footprintName; bool nameChanged = m_footprintNameWhenLoaded != footprintName;
int likelyAttr = aFootprint->GetLikelyAttribute();
int setAttr = ( aFootprint->GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
// This is only valid if the footprint doesn't have FP_SMD and FP_THROUGH_HOLE set
// Which is, unfortunately, possible in theory but not in the UI (I think)
if( likelyAttr != setAttr )
{
wxString msg;
if( likelyAttr == FP_THROUGH_HOLE )
{
msg.Printf( _( "Your footprint has plated through hole pads but "
"its type is set to \"%s\"" ), aFootprint->GetTypeName() );
}
else if( likelyAttr == FP_SMD )
{
msg.Printf( _( "Your footprint has SMD pads but "
"its type is set to \"%s\"" ), aFootprint->GetTypeName() );
}
else
{
msg.Printf( _( "Your footprint has no SMD or plated through hole pads but "
"its type is set to \"%s\"" ), aFootprint->GetTypeName() );
}
KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
errorDlg.SetOKLabel( _( "Continue" ) );
errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
if( errorDlg.ShowModal() == wxID_CANCEL )
return false;
}
if( aFootprint->GetLink() != niluuid ) if( aFootprint->GetLink() != niluuid )
{ {