976 lines
30 KiB
C++
976 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::FindUnit( size_t aIndex, int aUnit, bool aIncludeNew ) const
|
|
{
|
|
int NumRef = m_flatList[aIndex].m_numRef;
|
|
|
|
for( size_t ii = 0; ii < m_flatList.size(); ii++ )
|
|
{
|
|
if( ( aIndex == ii )
|
|
|| ( m_flatList[ii].m_isNew && !aIncludeNew )
|
|
|| ( m_flatList[ii].m_numRef != NumRef )
|
|
|| ( m_flatList[aIndex].CompareRef( m_flatList[ii] ) != 0 ) )
|
|
continue;
|
|
|
|
if( m_flatList[ii].m_unit == aUnit )
|
|
return (int) ii;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
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
|
|
for( size_t i = originalSize; i < ( aAdditionalRefs.GetCount() + originalSize ); i++ )
|
|
RemoveItem( originalSize );
|
|
|
|
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;
|
|
}
|