mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
In large designs with thousands of duplicate elements (e.g. power symbols), we need to be smart about vector management. Removing all but the first few items should be batched and not element at a time Ref: KSC-885
955 lines
30 KiB
C++
955 lines
30 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2018 jean-pierre Charras <jp.charras at wanadoo.fr>
|
|
* Copyright (C) 1992-2011 Wayne Stambaugh <stambaughw@gmail.com>
|
|
* Copyright (C) 1992-2023 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
|
|
*/
|
|
|
|
/**
|
|
* @file sch_reference_list.cpp
|
|
* @brief functions to create a symbol flat list and to annotate schematic.
|
|
*/
|
|
|
|
#include <sch_reference_list.h>
|
|
#include <core/kicad_algo.h>
|
|
|
|
#include <wx/regex.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
|
|
#include <string_utils.h>
|
|
#include <erc_settings.h>
|
|
#include <sch_symbol.h>
|
|
#include <sch_edit_frame.h>
|
|
|
|
|
|
void SCH_REFERENCE_LIST::RemoveItem( unsigned int aIndex )
|
|
{
|
|
if( aIndex < m_flatList.size() )
|
|
m_flatList.erase( m_flatList.begin() + aIndex );
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::Contains( const SCH_REFERENCE& aItem ) const
|
|
{
|
|
for( unsigned ii = 0; ii < GetCount(); ii++ )
|
|
{
|
|
if( m_flatList[ii].IsSameInstance( aItem ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::sortByXPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 )
|
|
{
|
|
int ii = item1.CompareRef( item2 );
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_sheetNum - item2.m_sheetNum;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
|
|
|
|
if( ii == 0 )
|
|
return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
|
|
else
|
|
return ii < 0;
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::sortByYPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 )
|
|
{
|
|
int ii = item1.CompareRef( item2 );
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_sheetNum - item2.m_sheetNum;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
|
|
|
|
if( ii == 0 )
|
|
return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
|
|
else
|
|
return ii < 0;
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::sortByRefAndValue( const SCH_REFERENCE& item1,
|
|
const SCH_REFERENCE& item2 )
|
|
{
|
|
int ii = item1.CompareRef( item2 );
|
|
|
|
if( ii == 0 )
|
|
ii = item1.CompareValue( item2 );
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_unit - item2.m_unit;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_sheetNum - item2.m_sheetNum;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
|
|
|
|
if( ii == 0 )
|
|
return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
|
|
else
|
|
return ii < 0;
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::sortByReferenceOnly( const SCH_REFERENCE& item1,
|
|
const SCH_REFERENCE& item2 )
|
|
{
|
|
int ii = StrNumCmp( item1.GetRef(), item2.GetRef(), false );
|
|
|
|
if( ii == 0 )
|
|
ii = item1.m_unit - item2.m_unit;
|
|
|
|
if( ii == 0 )
|
|
return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
|
|
else
|
|
return ii < 0;
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE_LIST::sortByTimeStamp( const SCH_REFERENCE& item1,
|
|
const SCH_REFERENCE& item2 )
|
|
{
|
|
int ii = item1.m_sheetPath.Cmp( item2.m_sheetPath );
|
|
|
|
if( ii == 0 )
|
|
return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
|
|
else
|
|
return ii < 0;
|
|
}
|
|
|
|
|
|
int SCH_REFERENCE_LIST::FindRefByFullPath( const wxString& aFullPath ) const
|
|
{
|
|
for( size_t i = 0; i < m_flatList.size(); ++i )
|
|
{
|
|
if( m_flatList[i].GetFullPath() == aFullPath )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int SCH_REFERENCE_LIST::FindRef( const wxString& aRef ) const
|
|
{
|
|
for( size_t i = 0; i < m_flatList.size(); ++i )
|
|
{
|
|
if( m_flatList[i].GetRef() == aRef )
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE_LIST::GetRefsInUse( int aIndex, std::vector< int >& aIdList,
|
|
int aMinRefId ) const
|
|
{
|
|
aIdList.clear();
|
|
|
|
for( const SCH_REFERENCE& ref : m_flatList )
|
|
{
|
|
// Don't add new references to the list as we will reannotate those
|
|
if( m_flatList[aIndex].CompareRef( ref ) == 0 && ref.m_numRef >= aMinRefId && !ref.m_isNew )
|
|
aIdList.push_back( ref.m_numRef );
|
|
}
|
|
|
|
std::sort( aIdList.begin(), aIdList.end() );
|
|
|
|
// Ensure each reference number appears only once. If there are symbols with
|
|
// multiple parts per package the same number will be stored for each part.
|
|
alg::remove_duplicates( aIdList );
|
|
}
|
|
|
|
|
|
std::vector<int> SCH_REFERENCE_LIST::GetUnitsMatchingRef( const SCH_REFERENCE& aRef ) const
|
|
{
|
|
std::vector<int> unitsList;
|
|
|
|
// Always add this reference to the list
|
|
unitsList.push_back( aRef.m_unit );
|
|
|
|
for( SCH_REFERENCE ref : m_flatList )
|
|
{
|
|
if( ref.CompareValue( aRef ) != 0 )
|
|
continue;
|
|
|
|
if( ref.CompareLibName( aRef ) != 0 )
|
|
continue;
|
|
|
|
// Split if needed before comparing ref and number
|
|
if( ref.IsSplitNeeded() )
|
|
ref.Split();
|
|
|
|
if( ref.CompareRef( aRef ) != 0 )
|
|
continue;
|
|
|
|
if( ref.m_numRef != aRef.m_numRef )
|
|
continue;
|
|
|
|
unitsList.push_back( ref.m_unit );
|
|
}
|
|
|
|
std::sort( unitsList.begin(), unitsList.end() );
|
|
|
|
// Ensure each reference number appears only once. If there are symbols with
|
|
// multiple parts per package the same number will be stored for each part.
|
|
alg::remove_duplicates( unitsList );
|
|
|
|
return unitsList;
|
|
}
|
|
|
|
|
|
int SCH_REFERENCE_LIST::FindFirstUnusedReference( const SCH_REFERENCE& aRef, int aMinValue,
|
|
const std::vector<int>& aRequiredUnits ) const
|
|
{
|
|
// Create a map of references indexed by reference number, only including those with the same
|
|
// reference prefix as aRef
|
|
std::map<int, std::vector<SCH_REFERENCE>> refNumberMap;
|
|
|
|
for( const SCH_REFERENCE& ref : m_flatList )
|
|
{
|
|
// search only for the current reference prefix:
|
|
if( ref.CompareRef( aRef ) != 0 )
|
|
continue;
|
|
|
|
if( ref.m_isNew )
|
|
continue; // It will be reannotated
|
|
|
|
refNumberMap[ref.m_numRef].push_back( ref );
|
|
}
|
|
|
|
// Start at the given minimum value
|
|
int minFreeNumber = aMinValue;
|
|
|
|
for( ; refNumberMap[minFreeNumber].size() > 0; ++minFreeNumber )
|
|
{
|
|
auto isNumberInUse = [&]() -> bool
|
|
{
|
|
for( const int& unit : aRequiredUnits )
|
|
{
|
|
for( const SCH_REFERENCE& ref : refNumberMap[minFreeNumber] )
|
|
{
|
|
if( ref.CompareLibName( aRef ) || ref.CompareValue( aRef )
|
|
|| ref.GetUnit() == unit )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
if( !isNumberInUse() )
|
|
return minFreeNumber;
|
|
}
|
|
|
|
return minFreeNumber;
|
|
}
|
|
|
|
|
|
std::vector<SCH_SYMBOL_INSTANCE> SCH_REFERENCE_LIST::GetSymbolInstances() const
|
|
{
|
|
std::vector<SCH_SYMBOL_INSTANCE> retval;
|
|
|
|
for( const SCH_REFERENCE& ref : m_flatList )
|
|
{
|
|
SCH_SYMBOL_INSTANCE instance;
|
|
instance.m_Path = ref.GetSheetPath().Path();
|
|
instance.m_Reference = ref.GetRef();
|
|
instance.m_Unit = ref.GetUnit();
|
|
|
|
retval.push_back( instance );
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
int SCH_REFERENCE_LIST::createFirstFreeRefId( std::vector<int>& aIdList, int aFirstValue )
|
|
{
|
|
int expectedId = aFirstValue;
|
|
|
|
// We search for expected Id a value >= aFirstValue.
|
|
// Skip existing Id < aFirstValue
|
|
unsigned ii = 0;
|
|
|
|
for( ; ii < aIdList.size(); ii++ )
|
|
{
|
|
if( expectedId <= aIdList[ii] )
|
|
break;
|
|
}
|
|
|
|
// Ids are sorted by increasing value, from aFirstValue
|
|
// So we search from aFirstValue the first not used value, i.e. the first hole in list.
|
|
for( ; ii < aIdList.size(); ii++ )
|
|
{
|
|
if( expectedId != aIdList[ii] ) // This id is not yet used.
|
|
{
|
|
// Insert this free Id, in order to keep list sorted
|
|
aIdList.insert( aIdList.begin() + ii, expectedId );
|
|
return expectedId;
|
|
}
|
|
|
|
expectedId++;
|
|
}
|
|
|
|
// All existing Id are tested, and all values are found in use.
|
|
// So Create a new one.
|
|
aIdList.push_back( expectedId );
|
|
return expectedId;
|
|
}
|
|
|
|
|
|
// A helper function to build a full reference string of a SCH_REFERENCE item
|
|
wxString buildFullReference( const SCH_REFERENCE& aItem, int aUnitNumber = -1 )
|
|
{
|
|
wxString fullref;
|
|
fullref = aItem.GetRef() + aItem.GetRefNumber();
|
|
|
|
if( aUnitNumber < 0 )
|
|
fullref << ".." << aItem.GetUnit();
|
|
else
|
|
fullref << ".." << aUnitNumber;
|
|
|
|
return fullref;
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE_LIST::ReannotateByOptions( ANNOTATE_ORDER_T aSortOption,
|
|
ANNOTATE_ALGO_T aAlgoOption,
|
|
int aStartNumber,
|
|
const SCH_REFERENCE_LIST& aAdditionalRefs,
|
|
bool aStartAtCurrent,
|
|
SCH_SHEET_LIST* aHierarchy )
|
|
{
|
|
SplitReferences();
|
|
|
|
// All multi-unit symbols always locked to ensure consistent re-annotation
|
|
SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
|
|
|
|
for( size_t i = 0; i < GetCount(); i++ )
|
|
{
|
|
SCH_REFERENCE& ref = m_flatList[i];
|
|
wxString refstr = ref.GetSymbol()->GetRef( &ref.GetSheetPath() );
|
|
|
|
// Update sheet numbers based on the reference's sheet's position within the full
|
|
// hierarchy; we do this now before we annotate so annotation by sheet number * X
|
|
// works correctly.
|
|
if( aHierarchy )
|
|
{
|
|
SCH_SHEET_PATH* path = aHierarchy->FindSheetForPath( &ref.GetSheetPath() );
|
|
wxASSERT_MSG( path, wxT( "Attempting to annotate item on sheet not part of the hierarchy?" ) );
|
|
|
|
ref.SetSheetNumber( path->GetVirtualPageNumber() );
|
|
}
|
|
|
|
// Never lock unassigned references
|
|
if( refstr[refstr.Len() - 1] == '?' )
|
|
continue;
|
|
|
|
ref.m_isNew = true; // We want to reannotate all references
|
|
|
|
lockedSymbols[refstr].AddItem( ref );
|
|
}
|
|
|
|
AnnotateByOptions( aSortOption, aAlgoOption, aStartNumber, lockedSymbols, aAdditionalRefs,
|
|
aStartAtCurrent );
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE_LIST::ReannotateDuplicates( const SCH_REFERENCE_LIST& aAdditionalReferences )
|
|
{
|
|
ReannotateByOptions( UNSORTED, INCREMENTAL_BY_REF, 0, aAdditionalReferences, true, nullptr );
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE_LIST::AnnotateByOptions( ANNOTATE_ORDER_T aSortOption,
|
|
ANNOTATE_ALGO_T aAlgoOption,
|
|
int aStartNumber,
|
|
SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap,
|
|
const SCH_REFERENCE_LIST& aAdditionalRefs,
|
|
bool aStartAtCurrent )
|
|
{
|
|
switch( aSortOption )
|
|
{
|
|
default:
|
|
case SORT_BY_X_POSITION: SortByXCoordinate(); break;
|
|
case SORT_BY_Y_POSITION: SortByYCoordinate(); break;
|
|
}
|
|
|
|
bool useSheetNum;
|
|
int idStep;
|
|
|
|
switch( aAlgoOption )
|
|
{
|
|
default:
|
|
case INCREMENTAL_BY_REF:
|
|
useSheetNum = false;
|
|
idStep = 1;
|
|
break;
|
|
|
|
case SHEET_NUMBER_X_100:
|
|
useSheetNum = true;
|
|
idStep = 100;
|
|
aStartAtCurrent = false; // Not implemented for sheet # * 100
|
|
break;
|
|
|
|
case SHEET_NUMBER_X_1000:
|
|
useSheetNum = true;
|
|
idStep = 1000;
|
|
aStartAtCurrent = false; // Not implemented for sheet # * 1000
|
|
break;
|
|
}
|
|
|
|
Annotate( useSheetNum, idStep, aStartNumber, aLockedUnitMap, aAdditionalRefs, aStartAtCurrent );
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, int aStartNumber,
|
|
SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap,
|
|
const SCH_REFERENCE_LIST& aAdditionalRefs, bool aStartAtCurrent )
|
|
{
|
|
if ( m_flatList.size() == 0 )
|
|
return;
|
|
|
|
size_t originalSize = GetCount();
|
|
|
|
// For multi units symbols, store the list of already used full references.
|
|
// The algorithm tries to allocate the new reference to symbols having the same
|
|
// old reference.
|
|
// This algo works fine as long as the previous annotation has no duplicates.
|
|
// But when a hierarchy is reannotated with this option, the previous annotation can
|
|
// have duplicate references, and obviously we must fix these duplicate.
|
|
// therefore do not try to allocate a full reference more than once when trying
|
|
// to keep this order of multi units.
|
|
// inUseRefs keep trace of previously allocated references
|
|
std::unordered_set<wxString> inUseRefs;
|
|
|
|
for( size_t i = 0; i < aAdditionalRefs.GetCount(); i++ )
|
|
{
|
|
SCH_REFERENCE additionalRef = aAdditionalRefs[i];
|
|
additionalRef.Split();
|
|
|
|
// Add the additional reference to the multi-unit set if annotated
|
|
if( !additionalRef.m_isNew )
|
|
inUseRefs.insert( buildFullReference( additionalRef ) );
|
|
|
|
// We don't want to reannotate the additional references even if not annotated
|
|
// so we change the m_isNew flag to be false after splitting
|
|
additionalRef.m_isNew = false;
|
|
AddItem( additionalRef ); //add to this container
|
|
}
|
|
|
|
int LastReferenceNumber = 0;
|
|
|
|
/* calculate index of the first symbol with the same reference prefix
|
|
* than the current symbol. All symbols having the same reference
|
|
* prefix will receive a reference number with consecutive values:
|
|
* IC .. will be set to IC4, IC4, IC5 ...
|
|
*/
|
|
unsigned first = 0;
|
|
|
|
// calculate the last used number for this reference prefix:
|
|
int minRefId;
|
|
|
|
// when using sheet number, ensure ref number >= sheet number* aSheetIntervalId
|
|
if( aUseSheetNum )
|
|
minRefId = m_flatList[first].m_sheetNum * aSheetIntervalId + 1;
|
|
else
|
|
minRefId = aStartNumber + 1;
|
|
|
|
|
|
for( unsigned ii = 0; ii < m_flatList.size(); ii++ )
|
|
{
|
|
auto& ref_unit = m_flatList[ii];
|
|
|
|
if( ref_unit.m_flag )
|
|
continue;
|
|
|
|
// Check whether this symbol is in aLockedUnitMap.
|
|
SCH_REFERENCE_LIST* lockedList = nullptr;
|
|
|
|
for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : aLockedUnitMap )
|
|
{
|
|
unsigned n_refs = pair.second.GetCount();
|
|
|
|
for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI )
|
|
{
|
|
SCH_REFERENCE &thisRef = pair.second[thisRefI];
|
|
|
|
if( thisRef.IsSameInstance( ref_unit ) )
|
|
{
|
|
lockedList = &pair.second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( lockedList != nullptr )
|
|
break;
|
|
}
|
|
|
|
if( ( m_flatList[first].CompareRef( ref_unit ) != 0 )
|
|
|| ( aUseSheetNum && ( m_flatList[first].m_sheetNum != ref_unit.m_sheetNum ) ) )
|
|
{
|
|
// New reference found: we need a new ref number for this reference
|
|
first = ii;
|
|
|
|
// when using sheet number, ensure ref number >= sheet number* aSheetIntervalId
|
|
if( aUseSheetNum )
|
|
minRefId = ref_unit.m_sheetNum * aSheetIntervalId + 1;
|
|
else
|
|
minRefId = aStartNumber + 1;
|
|
}
|
|
|
|
// Find references greater than current reference (unless not annotated)
|
|
if( aStartAtCurrent && ref_unit.m_numRef > 0 )
|
|
minRefId = ref_unit.m_numRef;
|
|
|
|
wxCHECK( ref_unit.GetLibPart(), /* void */ );
|
|
|
|
// Annotation of one part per package symbols (trivial case).
|
|
if( ref_unit.GetLibPart()->GetUnitCount() <= 1 )
|
|
{
|
|
if( ref_unit.m_isNew )
|
|
{
|
|
std::vector<int> idList;
|
|
GetRefsInUse( first, idList, minRefId );
|
|
LastReferenceNumber = createFirstFreeRefId( idList, minRefId );
|
|
ref_unit.m_numRef = LastReferenceNumber;
|
|
}
|
|
|
|
ref_unit.m_flag = 1;
|
|
ref_unit.m_isNew = false;
|
|
continue;
|
|
}
|
|
|
|
// If this symbol is in aLockedUnitMap, copy the annotation to all
|
|
// symbols that are not it
|
|
if( lockedList != nullptr )
|
|
{
|
|
unsigned n_refs = lockedList->GetCount();
|
|
std::vector<int> units = lockedList->GetUnitsMatchingRef( ref_unit );
|
|
|
|
if( ref_unit.m_isNew )
|
|
{
|
|
LastReferenceNumber = FindFirstUnusedReference( ref_unit, minRefId, units );
|
|
ref_unit.m_numRef = LastReferenceNumber;
|
|
ref_unit.m_isNew = false;
|
|
ref_unit.m_flag = 1;
|
|
}
|
|
|
|
for( unsigned lockedRefI = 0; lockedRefI < n_refs; ++lockedRefI )
|
|
{
|
|
SCH_REFERENCE& lockedRef = ( *lockedList )[lockedRefI];
|
|
|
|
if( lockedRef.IsSameInstance( ref_unit ) )
|
|
{
|
|
// This is the symbol we're currently annotating. Hold the unit!
|
|
ref_unit.m_unit = lockedRef.m_unit;
|
|
|
|
// lock this new full reference
|
|
inUseRefs.insert( buildFullReference( ref_unit ) );
|
|
}
|
|
|
|
if( lockedRef.CompareValue( ref_unit ) != 0 )
|
|
continue;
|
|
|
|
if( lockedRef.CompareLibName( ref_unit ) != 0 )
|
|
continue;
|
|
|
|
// Find the matching symbol
|
|
for( unsigned jj = ii + 1; jj < m_flatList.size(); jj++ )
|
|
{
|
|
if( !lockedRef.IsSameInstance( m_flatList[jj] ) )
|
|
continue;
|
|
|
|
wxString ref_candidate = buildFullReference( ref_unit, lockedRef.m_unit );
|
|
|
|
// propagate the new reference and unit selection to the "old" symbol,
|
|
// if this new full reference is not already used (can happens when initial
|
|
// multiunits symbols have duplicate references)
|
|
if( inUseRefs.find( ref_candidate ) == inUseRefs.end() )
|
|
{
|
|
m_flatList[jj].m_numRef = ref_unit.m_numRef;
|
|
m_flatList[jj].m_isNew = false;
|
|
m_flatList[jj].m_flag = 1;
|
|
|
|
// lock this new full reference
|
|
inUseRefs.insert( ref_candidate );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( ref_unit.m_isNew )
|
|
{
|
|
// Reference belonging to multi-unit symbol that has not yet been annotated. We don't
|
|
// know what group this might belong to, so just find the first unused reference for
|
|
// this specific unit. The other units will be annotated in the following passes.
|
|
std::vector<int> units = { ref_unit.GetUnit() };
|
|
LastReferenceNumber = FindFirstUnusedReference( ref_unit, minRefId, units );
|
|
ref_unit.m_numRef = LastReferenceNumber;
|
|
ref_unit.m_isNew = false;
|
|
ref_unit.m_flag = 1;
|
|
}
|
|
}
|
|
|
|
// Remove aAdditionalRefs references
|
|
m_flatList.erase( m_flatList.begin() + originalSize, m_flatList.end() );
|
|
|
|
wxASSERT( originalSize == GetCount() ); // Make sure we didn't make a mistake
|
|
}
|
|
|
|
|
|
int SCH_REFERENCE_LIST::CheckAnnotation( ANNOTATION_ERROR_HANDLER aHandler )
|
|
{
|
|
int error = 0;
|
|
wxString tmp;
|
|
wxString tmp2;
|
|
wxString msg;
|
|
|
|
SortByRefAndValue();
|
|
|
|
// Split reference designators into name (prefix) and number: IC1 becomes IC, and 1.
|
|
SplitReferences();
|
|
|
|
// count not yet annotated items or annotation error.
|
|
for( unsigned ii = 0; ii < m_flatList.size(); ii++ )
|
|
{
|
|
msg.Empty();
|
|
tmp.Empty();
|
|
|
|
if( m_flatList[ii].m_isNew ) // Not yet annotated
|
|
{
|
|
if( m_flatList[ii].m_numRef >= 0 )
|
|
tmp << m_flatList[ii].m_numRef;
|
|
else
|
|
tmp = wxT( "?" );
|
|
|
|
if( ( m_flatList[ii].m_unit > 0 ) && ( m_flatList[ii].m_unit < 0x7FFFFFFF )
|
|
&& m_flatList[ii].GetLibPart()->GetUnitCount() > 1 )
|
|
{
|
|
msg.Printf( _( "Item not annotated: %s%s (unit %d)" ),
|
|
m_flatList[ii].GetRef(),
|
|
tmp,
|
|
m_flatList[ii].m_unit );
|
|
}
|
|
else
|
|
{
|
|
msg.Printf( _( "Item not annotated: %s%s" ), m_flatList[ii].GetRef(), tmp );
|
|
}
|
|
|
|
aHandler( ERCE_UNANNOTATED, msg, &m_flatList[ii], nullptr );
|
|
error++;
|
|
break;
|
|
}
|
|
|
|
// Error if unit number selected does not exist (greater than the number of units in
|
|
// the symbol). This can happen if a symbol has changed in a library after a
|
|
// previous annotation.
|
|
if( std::max( m_flatList[ii].GetLibPart()->GetUnitCount(), 1 ) < m_flatList[ii].m_unit )
|
|
{
|
|
if( m_flatList[ii].m_numRef >= 0 )
|
|
tmp << m_flatList[ii].m_numRef;
|
|
else
|
|
tmp = wxT( "?" );
|
|
|
|
msg.Printf( _( "Error: symbol %s%s%s (unit %d) exceeds units defined (%d)" ),
|
|
m_flatList[ii].GetRef(),
|
|
tmp,
|
|
LIB_SYMBOL::SubReference( m_flatList[ii].m_unit ),
|
|
m_flatList[ii].m_unit,
|
|
m_flatList[ii].GetLibPart()->GetUnitCount() );
|
|
|
|
aHandler( ERCE_EXTRA_UNITS, msg, &m_flatList[ii], nullptr );
|
|
error++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// count the duplicated elements (if all are annotated)
|
|
int imax = m_flatList.size() - 1;
|
|
|
|
for( int ii = 0; ii < imax; ii++ )
|
|
{
|
|
msg.Empty();
|
|
tmp.Empty();
|
|
tmp2.Empty();
|
|
|
|
SCH_REFERENCE& first = m_flatList[ii];
|
|
SCH_REFERENCE& second = m_flatList[ii + 1];
|
|
|
|
if( ( first.CompareRef( second ) != 0 )
|
|
|| ( first.m_numRef != second.m_numRef ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Same reference found. If same unit, error!
|
|
if( first.m_unit == second.m_unit )
|
|
{
|
|
if( first.m_numRef >= 0 )
|
|
tmp << first.m_numRef;
|
|
else
|
|
tmp = wxT( "?" );
|
|
|
|
msg.Printf( _( "Duplicate items %s%s%s\n" ),
|
|
first.GetRef(),
|
|
tmp,
|
|
first.GetLibPart()->GetUnitCount() > 1 ?
|
|
LIB_SYMBOL::SubReference( first.m_unit ) : wxString( wxT( "" ) ) );
|
|
|
|
aHandler( ERCE_DUPLICATE_REFERENCE, msg, &first, &m_flatList[ii+1] );
|
|
error++;
|
|
continue;
|
|
}
|
|
|
|
/* Test error if units are different but number of parts per package
|
|
* too high (ex U3 ( 1 part) and we find U3B this is an error) */
|
|
if( first.GetLibPart()->GetUnitCount() != second.GetLibPart()->GetUnitCount() )
|
|
{
|
|
if( first.m_numRef >= 0 )
|
|
tmp << first.m_numRef;
|
|
else
|
|
tmp = wxT( "?" );
|
|
|
|
if( second.m_numRef >= 0 )
|
|
tmp2 << second.m_numRef;
|
|
else
|
|
tmp2 = wxT( "?" );
|
|
|
|
msg.Printf( _( "Differing unit counts for item %s%s%s and %s%s%s\n" ),
|
|
first.GetRef(),
|
|
tmp,
|
|
LIB_SYMBOL::SubReference( first.m_unit ),
|
|
second.GetRef(),
|
|
tmp2,
|
|
LIB_SYMBOL::SubReference( second.m_unit ) );
|
|
|
|
aHandler( ERCE_DUPLICATE_REFERENCE, msg, &first, &second );
|
|
error++;
|
|
continue;
|
|
}
|
|
|
|
// Error if values are different between units, for the same reference
|
|
if( first.CompareValue( second ) != 0 )
|
|
{
|
|
msg.Printf( _( "Different values for %s%d%s (%s) and %s%d%s (%s)" ),
|
|
first.GetRef(),
|
|
first.m_numRef,
|
|
LIB_SYMBOL::SubReference( first.m_unit ),
|
|
first.m_value,
|
|
second.GetRef(),
|
|
second.m_numRef,
|
|
LIB_SYMBOL::SubReference( second.m_unit ),
|
|
second.m_value );
|
|
|
|
aHandler( ERCE_DIFFERENT_UNIT_VALUE, msg, &first, &second );
|
|
error++;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
SCH_REFERENCE::SCH_REFERENCE( SCH_SYMBOL* aSymbol, LIB_SYMBOL* aLibSymbol,
|
|
const SCH_SHEET_PATH& aSheetPath )
|
|
{
|
|
wxASSERT( aSymbol != nullptr );
|
|
|
|
m_rootSymbol = aSymbol;
|
|
m_libPart = aLibSymbol; // Warning: can be nullptr for orphan symbols
|
|
// (i.e. with a symbol library not found)
|
|
m_unit = aSymbol->GetUnitSelection( &aSheetPath );
|
|
m_footprint = aSymbol->GetFootprintFieldText( true, &aSheetPath, false );
|
|
m_sheetPath = aSheetPath;
|
|
m_isNew = false;
|
|
m_flag = 0;
|
|
m_symbolUuid = aSymbol->m_Uuid;
|
|
m_symbolPos = aSymbol->GetPosition();
|
|
m_sheetNum = 0;
|
|
|
|
if( aSymbol->GetRef( &aSheetPath ).IsEmpty() )
|
|
aSymbol->SetRef( &aSheetPath, wxT( "DefRef?" ) );
|
|
|
|
wxString ref = aSymbol->GetRef( &aSheetPath );
|
|
SetRef( ref );
|
|
|
|
m_numRef = -1;
|
|
|
|
if( aSymbol->GetValueFieldText( false, &aSheetPath, false ).IsEmpty() )
|
|
aSymbol->SetValueFieldText( wxT( "~" ) );
|
|
|
|
m_value = aSymbol->GetValueFieldText( false, &aSheetPath, false );
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE::Annotate()
|
|
{
|
|
if( m_numRef < 0 )
|
|
m_ref += '?';
|
|
else
|
|
m_ref = TO_UTF8( GetRef() << GetRefNumber() );
|
|
|
|
m_rootSymbol->SetRef( &m_sheetPath, FROM_UTF8( m_ref.c_str() ) );
|
|
m_rootSymbol->SetUnit( m_unit );
|
|
m_rootSymbol->SetUnitSelection( &m_sheetPath, m_unit );
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE::AlwaysAnnotate() const
|
|
{
|
|
wxCHECK( m_rootSymbol && m_rootSymbol->GetLibSymbolRef()
|
|
&& !m_rootSymbol->GetRef( &m_sheetPath ).IsEmpty(), false );
|
|
|
|
return m_rootSymbol->GetLibSymbolRef()->IsPower()
|
|
|| m_rootSymbol->GetRef( &m_sheetPath )[0] == wxUniChar( '#' );
|
|
}
|
|
|
|
|
|
void SCH_REFERENCE::Split()
|
|
{
|
|
std::string refText = GetRefStr();
|
|
|
|
m_numRef = -1;
|
|
|
|
int ll = refText.length() - 1;
|
|
|
|
if( refText[ll] == '?' )
|
|
{
|
|
m_isNew = true;
|
|
|
|
refText.erase( ll ); // delete last char
|
|
|
|
SetRefStr( refText );
|
|
}
|
|
else if( isdigit( refText[ll] ) == 0 )
|
|
{
|
|
m_isNew = true;
|
|
}
|
|
else
|
|
{
|
|
while( ll >= 0 )
|
|
{
|
|
if( (refText[ll] <= ' ' ) || isdigit( refText[ll] ) )
|
|
ll--;
|
|
else
|
|
{
|
|
if( isdigit( refText[ll + 1] ) )
|
|
{
|
|
// null terminated C string into cp
|
|
const char* cp = refText.c_str() + ll + 1;
|
|
|
|
m_numRef = atoi( cp );
|
|
}
|
|
|
|
refText.erase( ll+1 ); // delete from ll+1 to end
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetRefStr( refText );
|
|
}
|
|
}
|
|
|
|
|
|
bool SCH_REFERENCE::IsSplitNeeded()
|
|
{
|
|
std::string refText = GetRefStr();
|
|
|
|
int ll = refText.length() - 1;
|
|
|
|
return ( refText[ll] == '?' ) || isdigit( refText[ll] );
|
|
}
|
|
|
|
|
|
wxString SCH_REFERENCE_LIST::Shorthand( std::vector<SCH_REFERENCE> aList,
|
|
const wxString& refDelimiter,
|
|
const wxString& refRangeDelimiter )
|
|
{
|
|
wxString retVal;
|
|
size_t i = 0;
|
|
|
|
while( i < aList.size() )
|
|
{
|
|
wxString ref = aList[ i ].GetRef();
|
|
int numRef = aList[ i ].m_numRef;
|
|
|
|
size_t range = 1;
|
|
|
|
while( i + range < aList.size()
|
|
&& aList[ i + range ].GetRef() == ref
|
|
&& aList[ i + range ].m_numRef == int( numRef + range ) )
|
|
{
|
|
range++;
|
|
|
|
if( range == 2 && refRangeDelimiter.IsEmpty() )
|
|
break;
|
|
}
|
|
|
|
if( !retVal.IsEmpty() )
|
|
retVal << refDelimiter;
|
|
|
|
if( range == 1 )
|
|
{
|
|
retVal << ref << aList[ i ].GetRefNumber();
|
|
}
|
|
else if( range == 2 || refRangeDelimiter.IsEmpty() )
|
|
{
|
|
retVal << ref << aList[ i ].GetRefNumber();
|
|
retVal << refDelimiter;
|
|
retVal << ref << aList[ i + 1 ].GetRefNumber();
|
|
}
|
|
else
|
|
{
|
|
retVal << ref << aList[ i ].GetRefNumber();
|
|
retVal << refRangeDelimiter;
|
|
retVal << ref << aList[ i + ( range - 1 ) ].GetRefNumber();
|
|
}
|
|
|
|
i+= range;
|
|
}
|
|
|
|
return retVal;
|
|
}
|