EEschema: move symbol checker code in symbol_checker.cpp.

- Add some tests for Power symbols.
- Fix incorrect print format in some messages.
This commit is contained in:
jean-pierre charras 2022-04-16 18:55:04 +02:00
parent 2f2e956921
commit 65f7d9f97a
3 changed files with 337 additions and 239 deletions

View File

@ -230,6 +230,7 @@ set( EESCHEMA_SRCS
schematic_undo_redo.cpp
sheet.cpp
symbol_async_loader.cpp
symbol_checker.cpp
symbol_lib_table.cpp
symbol_library.cpp
symbol_tree_model_adapter.cpp

331
eeschema/symbol_checker.cpp Normal file
View File

@ -0,0 +1,331 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.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
*/
#include <vector>
#include <sch_symbol.h>
#include <base_units.h>
#include <math/util.h> // for KiROUND
// helper function to sort pins by pin num
static bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst );
/**
* Check a lib symbol to find incorrect settings
* Pins not on a valid grid
* Pins duplicated
* Conflict with pins at same location
* Incorrect Power Symbols
* @param aSymbol is the library symbol to check
* @param aMessages is a room to store error messages
* @param aGridForPins (in IU) is the grid to test pin positions ( >= 25 mils )
* should be 25, 50 or 100 mils (convered to IUs)
* @param aDisplayUnits is the unit to display coordinates in messages
*/
void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
int aGridForPins, EDA_UNITS aDisplayUnits )
{
if( !aSymbol )
return;
LIB_PINS pinList;
aSymbol->GetPins( pinList );
// Test for duplicates:
// Sort pins by pin num, so 2 duplicate pins
// (pins with the same number) will be consecutive in list
sort( pinList.begin(), pinList.end(), sort_by_pin_number );
// The minimal grid size allowed to place a pin is 25 mils
// the best grid size is 50 mils, but 25 mils is still usable
// this is because all aSymbols are using a 50 mils grid to place pins, and therefore
// the wires must be on the 50 mils grid
// So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
const int min_grid_size = Mils2iu( 25 );
const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
wxString msg;
for( unsigned ii = 1; ii < pinList.size(); ii++ )
{
LIB_PIN* pin = pinList[ii - 1];
LIB_PIN* next = pinList[ii];
if( pin->GetNumber() != next->GetNumber() || pin->GetConvert() != next->GetConvert() )
continue;
wxString pinName;
wxString nextName;
if( pin->GetName() != "~" && !pin->GetName().IsEmpty() )
pinName = " '" + pin->GetName() + "'";
if( next->GetName() != "~" && !next->GetName().IsEmpty() )
nextName = " '" + next->GetName() + "'";
if( aSymbol->HasConversion() && next->GetConvert() )
{
if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
" conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
" of converted." ),
next->GetNumber(),
nextName,
MessageTextFromValue( aDisplayUnits, next->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -next->GetPosition().y ),
pin->GetNumber(),
pin->GetName(),
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
" conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
" in units %s and %s of converted." ),
next->GetNumber(),
nextName,
MessageTextFromValue( aDisplayUnits, next->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
aSymbol->GetUnitReference( next->GetUnit() ),
aSymbol->GetUnitReference( pin->GetUnit() ) );
}
}
else
{
if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
" conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
next->GetNumber(),
nextName,
MessageTextFromValue( aDisplayUnits, next->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
" conflicts with pin %s%s at location <b>(%s, %s)</b>"
" in units %s and %s." ),
next->GetNumber(),
nextName,
MessageTextFromValue( aDisplayUnits, next->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
aSymbol->GetUnitReference( next->GetUnit() ),
aSymbol->GetUnitReference( pin->GetUnit() ) );
}
}
msg += wxT( "<br><br>" );
aMessages.push_back( msg );
}
// Test for a valid power aSymbol.
// A valid power aSymbol has only one unit, no convert and one pin.
// And this pin should be PT_POWER_IN (invisible to be automatically connected)
// or PT_POWER_OUT for a power flag
if( aSymbol->IsPower() )
{
if( aSymbol->GetUnitCount() != 1 )
{
msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
aMessages.push_back( msg );
}
if( aSymbol->HasConversion() )
{
msg.Printf( _( "<b>A Power Symbol should have no convert option</b><br><br>" ) );
aMessages.push_back( msg );
}
if( pinList.size() != 1 )
{
msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
aMessages.push_back( msg );
}
LIB_PIN* pin = pinList[0];
if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
&& pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
{
msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
"Only a input or output power pin has meaning<br><br>" ) );
aMessages.push_back( msg );
}
if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && pin->IsVisible() )
{
msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
"Only invisible input power pins are automatically connected<br><br>" ) );
aMessages.push_back( msg );
}
}
for( LIB_PIN* pin : pinList )
{
wxString pinName = pin->GetName();
if( pinName.IsEmpty() || pinName == "~" )
pinName = "";
else
pinName = "'" + pinName + "'";
if( !aSymbol->IsPower()
&& pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
&& !pin->IsVisible() )
{
// hidden power pin
if( aSymbol->HasConversion() && pin->GetConvert() )
{
if( aSymbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
else
{
if( aSymbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
msg += wxT( "<br>" );
msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
msg += wxT( "<br><br>" );
aMessages.push_back( msg );
}
if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
|| ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
{
// pin is off grid
if( aSymbol->HasConversion() && pin->GetConvert() )
{
if( aSymbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
" of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%.3s, %.3s)</b>"
" in unit %c of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
else
{
if( aSymbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( aDisplayUnits, pin->GetPosition().x ),
MessageTextFromValue( aDisplayUnits, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
msg += wxT( "<br><br>" );
aMessages.push_back( msg );
}
}
}
bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst )
{
// Use number as primary key
int test = ref->GetNumber().Cmp( tst->GetNumber() );
// Use DeMorgan variant as secondary key
if( test == 0 )
test = ref->GetConvert() - tst->GetConvert();
// Use unit as tertiary key
if( test == 0 )
test = ref->GetUnit() - tst->GetUnit();
return test < 0;
}

View File

@ -179,255 +179,21 @@ int EE_INSPECTION_TOOL::ExcludeMarker( const TOOL_EVENT& aEvent )
}
// helper function to sort pins by pin num
bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst )
{
// Use number as primary key
int test = ref->GetNumber().Cmp( tst->GetNumber() );
// Use DeMorgan variant as secondary key
if( test == 0 )
test = ref->GetConvert() - tst->GetConvert();
// Use unit as tertiary key
if( test == 0 )
test = ref->GetUnit() - tst->GetUnit();
return test < 0;
}
extern void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
int aGridForPins, EDA_UNITS aDisplayUnits );
int EE_INSPECTION_TOOL::CheckSymbol( const TOOL_EVENT& aEvent )
{
LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
EDA_UNITS units = m_frame->GetUserUnits();
if( !symbol )
return 0;
LIB_PINS pinList;
symbol->GetPins( pinList );
// Test for duplicates:
// Sort pins by pin num, so 2 duplicate pins
// (pins with the same number) will be consecutive in list
sort( pinList.begin(), pinList.end(), sort_by_pin_number );
// The minimal grid size allowed to place a pin is 25 mils
// the best grid size is 50 mils, but 25 mils is still usable
// this is because all symbols are using a 50 mils grid to place pins, and therefore
// the wires must be on the 50 mils grid
// So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
const int min_grid_size = Mils2iu( 25 );
const int grid_size = KiROUND( getView()->GetGAL()->GetGridSize().x );
const int clamped_grid_size = ( grid_size < min_grid_size ) ? min_grid_size : grid_size;
EDA_UNITS units = m_frame->GetUserUnits();
std::vector<wxString> messages;
wxString msg;
const int grid_size = KiROUND( getView()->GetGAL()->GetGridSize().x );
for( unsigned ii = 1; ii < pinList.size(); ii++ )
{
LIB_PIN* pin = pinList[ii - 1];
LIB_PIN* next = pinList[ii];
if( pin->GetNumber() != next->GetNumber() || pin->GetConvert() != next->GetConvert() )
continue;
wxString pinName;
wxString nextName;
if( pin->GetName() != "~" && !pin->GetName().IsEmpty() )
pinName = " '" + pin->GetName() + "'";
if( next->GetName() != "~" && !next->GetName().IsEmpty() )
nextName = " '" + next->GetName() + "'";
if( symbol->HasConversion() && next->GetConvert() )
{
if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
" conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
" of converted." ),
next->GetNumber(),
nextName,
MessageTextFromValue( units, next->GetPosition().x ),
MessageTextFromValue( units, -next->GetPosition().y ),
pin->GetNumber(),
pin->GetName(),
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%.3f, %.3f)</b>"
" conflicts with pin %s%s at location <b>(%.3f, %.3f)</b>"
" in units %c and %c of converted." ),
next->GetNumber(),
nextName,
MessageTextFromValue( units, next->GetPosition().x ),
MessageTextFromValue( units, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
symbol->GetUnitReference( next->GetUnit() ),
symbol->GetUnitReference( pin->GetUnit() ) );
}
}
else
{
if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
" conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
next->GetNumber(),
nextName,
MessageTextFromValue( units, next->GetPosition().x ),
MessageTextFromValue( units, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
" conflicts with pin %s%s at location <b>(%s, %s)</b>"
" in units %c and %c." ),
next->GetNumber(),
nextName,
MessageTextFromValue( units, next->GetPosition().x ),
MessageTextFromValue( units, -next->GetPosition().y ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
symbol->GetUnitReference( next->GetUnit() ),
symbol->GetUnitReference( pin->GetUnit() ) );
}
}
msg += wxT( "<br><br>" );
messages.push_back( msg );
}
for( LIB_PIN* pin : pinList )
{
wxString pinName = pin->GetName();
if( pinName.IsEmpty() || pinName == "~" )
pinName = "";
else
pinName = "'" + pinName + "'";
if( !symbol->IsPower()
&& pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
&& !pin->IsVisible() )
{
// hidden power pin
if( symbol->HasConversion() && pin->GetConvert() )
{
if( symbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
else
{
if( symbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
msg += wxT( "<br>" );
msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
msg += wxT( "<br><br>" );
messages.push_back( msg );
}
if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
|| ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
{
// pin is off grid
if( symbol->HasConversion() && pin->GetConvert() )
{
if( symbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
" of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%.3s, %.3s)</b>"
" in unit %c of converted." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
else
{
if( symbol->GetUnitCount() <= 1 )
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ) );
}
else
{
msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
" in unit %c." ),
pin->GetNumber(),
pinName,
MessageTextFromValue( units, pin->GetPosition().x ),
MessageTextFromValue( units, -pin->GetPosition().y ),
'A' + pin->GetUnit() - 1 );
}
}
msg += wxT( "<br><br>" );
messages.push_back( msg );
}
}
CheckLibSymbol( symbol, messages, grid_size, units );
if( messages.empty() )
{