You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

942 lines
29 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2018 jean-pierre Charras <jp.charras at wanadoo.fr>
  5. * Copyright (C) 1992-2011 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file sch_reference_list.cpp
  27. * @brief functions to create a symbol flat list and to annotate schematic.
  28. */
  29. #include <sch_reference_list.h>
  30. #include <core/kicad_algo.h>
  31. #include <wx/regex.h>
  32. #include <algorithm>
  33. #include <vector>
  34. #include <unordered_set>
  35. #include <string_utils.h>
  36. #include <erc/erc_settings.h>
  37. #include <refdes_tracker.h>
  38. #include <sch_symbol.h>
  39. #include <sch_edit_frame.h>
  40. void SCH_REFERENCE_LIST::RemoveItem( unsigned int aIndex )
  41. {
  42. if( aIndex < m_flatList.size() )
  43. m_flatList.erase( m_flatList.begin() + aIndex );
  44. }
  45. bool SCH_REFERENCE_LIST::Contains( const SCH_REFERENCE& aItem ) const
  46. {
  47. for( unsigned ii = 0; ii < GetCount(); ii++ )
  48. {
  49. if( m_flatList[ii].IsSameInstance( aItem ) )
  50. return true;
  51. }
  52. return false;
  53. }
  54. bool SCH_REFERENCE_LIST::sortByXPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 )
  55. {
  56. int ii = item1.CompareRef( item2 );
  57. if( ii == 0 )
  58. ii = item1.m_sheetNum - item2.m_sheetNum;
  59. if( ii == 0 )
  60. ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
  61. if( ii == 0 )
  62. ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
  63. if( ii == 0 )
  64. return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
  65. else
  66. return ii < 0;
  67. }
  68. bool SCH_REFERENCE_LIST::sortByYPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 )
  69. {
  70. int ii = item1.CompareRef( item2 );
  71. if( ii == 0 )
  72. ii = item1.m_sheetNum - item2.m_sheetNum;
  73. if( ii == 0 )
  74. ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
  75. if( ii == 0 )
  76. ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
  77. if( ii == 0 )
  78. return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
  79. else
  80. return ii < 0;
  81. }
  82. bool SCH_REFERENCE_LIST::sortByRefAndValue( const SCH_REFERENCE& item1,
  83. const SCH_REFERENCE& item2 )
  84. {
  85. int ii = item1.CompareRef( item2 );
  86. if( ii == 0 )
  87. ii = item1.CompareValue( item2 );
  88. if( ii == 0 )
  89. ii = item1.m_unit - item2.m_unit;
  90. if( ii == 0 )
  91. ii = item1.m_sheetNum - item2.m_sheetNum;
  92. if( ii == 0 )
  93. ii = item1.m_symbolPos.x - item2.m_symbolPos.x;
  94. if( ii == 0 )
  95. ii = item1.m_symbolPos.y - item2.m_symbolPos.y;
  96. if( ii == 0 )
  97. return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
  98. else
  99. return ii < 0;
  100. }
  101. bool SCH_REFERENCE_LIST::sortByReferenceOnly( const SCH_REFERENCE& item1,
  102. const SCH_REFERENCE& item2 )
  103. {
  104. int ii = StrNumCmp( item1.GetRef(), item2.GetRef(), false );
  105. if( ii == 0 )
  106. ii = item1.m_unit - item2.m_unit;
  107. if( ii == 0 )
  108. return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
  109. else
  110. return ii < 0;
  111. }
  112. bool SCH_REFERENCE_LIST::sortByTimeStamp( const SCH_REFERENCE& item1,
  113. const SCH_REFERENCE& item2 )
  114. {
  115. int ii = item1.m_sheetPath.Cmp( item2.m_sheetPath );
  116. if( ii == 0 )
  117. return item1.m_symbolUuid < item2.m_symbolUuid; // ensure a deterministic sort
  118. else
  119. return ii < 0;
  120. }
  121. bool SCH_REFERENCE_LIST::sortBySymbolPtr( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 )
  122. {
  123. return item1.m_rootSymbol < item2.m_rootSymbol;
  124. }
  125. int SCH_REFERENCE_LIST::FindRefByFullPath( const wxString& aFullPath ) const
  126. {
  127. for( size_t i = 0; i < m_flatList.size(); ++i )
  128. {
  129. if( m_flatList[i].GetFullPath() == aFullPath )
  130. return i;
  131. }
  132. return -1;
  133. }
  134. int SCH_REFERENCE_LIST::FindRef( const wxString& aRef ) const
  135. {
  136. for( size_t i = 0; i < m_flatList.size(); ++i )
  137. {
  138. if( m_flatList[i].GetRef() == aRef )
  139. return i;
  140. }
  141. return -1;
  142. }
  143. void SCH_REFERENCE_LIST::GetRefsInUse( int aIndex, std::vector< int >& aIdList,
  144. int aMinRefId ) const
  145. {
  146. aIdList.clear();
  147. for( const SCH_REFERENCE& ref : m_flatList )
  148. {
  149. // Don't add new references to the list as we will reannotate those
  150. if( m_flatList[aIndex].CompareRef( ref ) == 0 && ref.m_numRef >= aMinRefId && !ref.m_isNew )
  151. aIdList.push_back( ref.m_numRef );
  152. }
  153. std::sort( aIdList.begin(), aIdList.end() );
  154. // Ensure each reference number appears only once. If there are symbols with
  155. // multiple parts per package the same number will be stored for each part.
  156. alg::remove_duplicates( aIdList );
  157. }
  158. std::vector<int> SCH_REFERENCE_LIST::GetUnitsMatchingRef( const SCH_REFERENCE& aRef ) const
  159. {
  160. std::vector<int> unitsList;
  161. // Always add this reference to the list
  162. unitsList.push_back( aRef.m_unit );
  163. for( SCH_REFERENCE ref : m_flatList )
  164. {
  165. if( ref.CompareValue( aRef ) != 0 )
  166. continue;
  167. if( ref.CompareLibName( aRef ) != 0 )
  168. continue;
  169. // Split if needed before comparing ref and number
  170. if( ref.IsSplitNeeded() )
  171. ref.Split();
  172. if( ref.CompareRef( aRef ) != 0 )
  173. continue;
  174. if( ref.m_numRef != aRef.m_numRef )
  175. continue;
  176. unitsList.push_back( ref.m_unit );
  177. }
  178. std::sort( unitsList.begin(), unitsList.end() );
  179. // Ensure each reference number appears only once. If there are symbols with
  180. // multiple parts per package the same number will be stored for each part.
  181. alg::remove_duplicates( unitsList );
  182. return unitsList;
  183. }
  184. int SCH_REFERENCE_LIST::FindFirstUnusedReference( const SCH_REFERENCE& aRef, int aMinValue,
  185. const std::vector<int>& aRequiredUnits ) const
  186. {
  187. // Create a map of references indexed by reference number, only including those with the same
  188. // reference prefix as aRef
  189. std::map<int, std::vector<SCH_REFERENCE>> refNumberMap;
  190. for( const SCH_REFERENCE& ref : m_flatList )
  191. {
  192. // search only for the current reference prefix:
  193. if( ref.CompareRef( aRef ) != 0 )
  194. continue;
  195. if( ref.m_isNew )
  196. continue; // It will be reannotated
  197. refNumberMap[ref.m_numRef].push_back( ref );
  198. }
  199. return m_refDesTracker->GetNextRefDesForUnits( aRef, refNumberMap, aRequiredUnits, aMinValue );
  200. }
  201. std::vector<SCH_SYMBOL_INSTANCE> SCH_REFERENCE_LIST::GetSymbolInstances() const
  202. {
  203. std::vector<SCH_SYMBOL_INSTANCE> retval;
  204. for( const SCH_REFERENCE& ref : m_flatList )
  205. {
  206. SCH_SYMBOL_INSTANCE instance;
  207. instance.m_Path = ref.GetSheetPath().Path();
  208. instance.m_Reference = ref.GetRef();
  209. instance.m_Unit = ref.GetUnit();
  210. retval.push_back( instance );
  211. }
  212. return retval;
  213. }
  214. // A helper function to build a full reference string of a SCH_REFERENCE item
  215. wxString buildFullReference( const SCH_REFERENCE& aItem, int aUnitNumber = -1 )
  216. {
  217. wxString fullref;
  218. fullref = aItem.GetRef() + aItem.GetRefNumber();
  219. if( aUnitNumber < 0 )
  220. fullref << ".." << aItem.GetUnit();
  221. else
  222. fullref << ".." << aUnitNumber;
  223. return fullref;
  224. }
  225. void SCH_REFERENCE_LIST::ReannotateByOptions( ANNOTATE_ORDER_T aSortOption,
  226. ANNOTATE_ALGO_T aAlgoOption,
  227. int aStartNumber,
  228. const SCH_REFERENCE_LIST& aAdditionalRefs,
  229. bool aStartAtCurrent,
  230. SCH_SHEET_LIST* aHierarchy )
  231. {
  232. SplitReferences();
  233. // All multi-unit symbols always locked to ensure consistent re-annotation
  234. SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
  235. for( size_t i = 0; i < GetCount(); i++ )
  236. {
  237. SCH_REFERENCE& ref = m_flatList[i];
  238. wxString refstr = ref.GetSymbol()->GetRef( &ref.GetSheetPath() );
  239. // Update sheet numbers based on the reference's sheet's position within the full
  240. // hierarchy; we do this now before we annotate so annotation by sheet number * X
  241. // works correctly.
  242. if( aHierarchy )
  243. {
  244. SCH_SHEET_PATH* path = aHierarchy->FindSheetForPath( &ref.GetSheetPath() );
  245. wxCHECK2_MSG( path, continue,
  246. wxS( "Attempting to annotate item on sheet not part of the "
  247. "hierarchy?" ) );
  248. ref.SetSheetNumber( path->GetVirtualPageNumber() );
  249. }
  250. // Never lock unassigned references
  251. if( refstr[refstr.Len() - 1] == '?' )
  252. continue;
  253. ref.m_isNew = true; // We want to reannotate all references
  254. lockedSymbols[refstr].AddItem( ref );
  255. }
  256. AnnotateByOptions( aSortOption, aAlgoOption, aStartNumber, lockedSymbols, aAdditionalRefs,
  257. aStartAtCurrent );
  258. }
  259. void SCH_REFERENCE_LIST::ReannotateDuplicates( const SCH_REFERENCE_LIST& aAdditionalReferences )
  260. {
  261. ReannotateByOptions( UNSORTED, INCREMENTAL_BY_REF, 0, aAdditionalReferences, true, nullptr );
  262. }
  263. void SCH_REFERENCE_LIST::AnnotateByOptions( ANNOTATE_ORDER_T aSortOption,
  264. ANNOTATE_ALGO_T aAlgoOption,
  265. int aStartNumber,
  266. const SCH_MULTI_UNIT_REFERENCE_MAP& aLockedUnitMap,
  267. const SCH_REFERENCE_LIST& aAdditionalRefs,
  268. bool aStartAtCurrent )
  269. {
  270. switch( aSortOption )
  271. {
  272. default:
  273. case SORT_BY_X_POSITION: SortByXCoordinate(); break;
  274. case SORT_BY_Y_POSITION: SortByYCoordinate(); break;
  275. }
  276. bool useSheetNum;
  277. int idStep;
  278. switch( aAlgoOption )
  279. {
  280. default:
  281. case INCREMENTAL_BY_REF:
  282. useSheetNum = false;
  283. idStep = 1;
  284. break;
  285. case SHEET_NUMBER_X_100:
  286. useSheetNum = true;
  287. idStep = 100;
  288. aStartAtCurrent = false; // Not implemented for sheet # * 100
  289. break;
  290. case SHEET_NUMBER_X_1000:
  291. useSheetNum = true;
  292. idStep = 1000;
  293. aStartAtCurrent = false; // Not implemented for sheet # * 1000
  294. break;
  295. }
  296. Annotate( useSheetNum, idStep, aStartNumber, aLockedUnitMap, aAdditionalRefs, aStartAtCurrent );
  297. }
  298. void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, int aStartNumber,
  299. const SCH_MULTI_UNIT_REFERENCE_MAP& aLockedUnitMap,
  300. const SCH_REFERENCE_LIST& aAdditionalRefs,
  301. bool aStartAtCurrent )
  302. {
  303. if( !m_refDesTracker )
  304. {
  305. wxLogError( wxS( "No reference tracker set for SCH_REFERENCE_LIST::Annotate()" ) );
  306. return;
  307. }
  308. if ( m_flatList.size() == 0 )
  309. return;
  310. size_t originalSize = GetCount();
  311. // For multi units symbols, store the list of already used full references.
  312. // The algorithm tries to allocate the new reference to symbols having the same
  313. // old reference.
  314. // This algo works fine as long as the previous annotation has no duplicates.
  315. // But when a hierarchy is reannotated with this option, the previous annotation can
  316. // have duplicate references, and obviously we must fix these duplicate.
  317. // therefore do not try to allocate a full reference more than once when trying
  318. // to keep this order of multi units.
  319. // inUseRefs keep trace of previously allocated references
  320. std::unordered_set<wxString> inUseRefs;
  321. for( size_t i = 0; i < aAdditionalRefs.GetCount(); i++ )
  322. {
  323. SCH_REFERENCE additionalRef = aAdditionalRefs[i];
  324. additionalRef.Split();
  325. // Add the additional reference to the multi-unit set if annotated
  326. if( !additionalRef.m_isNew )
  327. inUseRefs.insert( buildFullReference( additionalRef ) );
  328. // We don't want to reannotate the additional references even if not annotated
  329. // so we change the m_isNew flag to be false after splitting
  330. additionalRef.m_isNew = false;
  331. AddItem( additionalRef ); //add to this container
  332. }
  333. int LastReferenceNumber = 0;
  334. /* calculate index of the first symbol with the same reference prefix
  335. * than the current symbol. All symbols having the same reference
  336. * prefix will receive a reference number with consecutive values:
  337. * IC .. will be set to IC4, IC4, IC5 ...
  338. */
  339. unsigned first = 0;
  340. // calculate the last used number for this reference prefix:
  341. int minRefId;
  342. // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId
  343. if( aUseSheetNum )
  344. minRefId = m_flatList[first].m_sheetNum * aSheetIntervalId + 1;
  345. else
  346. minRefId = aStartNumber + 1;
  347. for( unsigned ii = 0; ii < m_flatList.size(); ii++ )
  348. {
  349. auto& ref_unit = m_flatList[ii];
  350. if( ref_unit.m_flag )
  351. continue;
  352. // Check whether this symbol is in aLockedUnitMap.
  353. const SCH_REFERENCE_LIST* lockedList = nullptr;
  354. for( const SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : aLockedUnitMap )
  355. {
  356. unsigned n_refs = pair.second.GetCount();
  357. for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI )
  358. {
  359. const SCH_REFERENCE &thisRef = pair.second[thisRefI];
  360. if( thisRef.IsSameInstance( ref_unit ) )
  361. {
  362. lockedList = &pair.second;
  363. break;
  364. }
  365. }
  366. if( lockedList != nullptr )
  367. break;
  368. }
  369. if( ( m_flatList[first].CompareRef( ref_unit ) != 0 )
  370. || ( aUseSheetNum && ( m_flatList[first].m_sheetNum != ref_unit.m_sheetNum ) ) )
  371. {
  372. // New reference found: we need a new ref number for this reference
  373. first = ii;
  374. // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId
  375. if( aUseSheetNum )
  376. minRefId = ref_unit.m_sheetNum * aSheetIntervalId + 1;
  377. else
  378. minRefId = aStartNumber + 1;
  379. }
  380. // Find references greater than current reference (unless not annotated)
  381. if( aStartAtCurrent && ref_unit.m_numRef > 0 )
  382. minRefId = ref_unit.m_numRef;
  383. wxCHECK( ref_unit.GetLibPart(), /* void */ );
  384. // Annotation of one part per package symbols (trivial case).
  385. if( ref_unit.GetLibPart()->GetUnitCount() <= 1 )
  386. {
  387. if( ref_unit.m_isNew )
  388. {
  389. LastReferenceNumber = FindFirstUnusedReference( ref_unit, minRefId, {} );
  390. ref_unit.m_numRef = LastReferenceNumber;
  391. ref_unit.m_numRefStr = ref_unit.formatRefStr( LastReferenceNumber );
  392. }
  393. ref_unit.m_flag = 1;
  394. ref_unit.m_isNew = false;
  395. continue;
  396. }
  397. // If this symbol is in aLockedUnitMap, copy the annotation to all
  398. // symbols that are not it
  399. if( lockedList != nullptr )
  400. {
  401. unsigned n_refs = lockedList->GetCount();
  402. std::vector<int> units = lockedList->GetUnitsMatchingRef( ref_unit );
  403. if( ref_unit.m_isNew )
  404. {
  405. LastReferenceNumber = FindFirstUnusedReference( ref_unit, minRefId, units );
  406. ref_unit.m_numRef = LastReferenceNumber;
  407. ref_unit.m_numRefStr = ref_unit.formatRefStr( LastReferenceNumber );
  408. ref_unit.m_isNew = false;
  409. ref_unit.m_flag = 1;
  410. }
  411. for( unsigned lockedRefI = 0; lockedRefI < n_refs; ++lockedRefI )
  412. {
  413. const SCH_REFERENCE& lockedRef = ( *lockedList )[lockedRefI];
  414. if( lockedRef.IsSameInstance( ref_unit ) )
  415. {
  416. // This is the symbol we're currently annotating. Hold the unit!
  417. ref_unit.m_unit = lockedRef.m_unit;
  418. // lock this new full reference
  419. inUseRefs.insert( buildFullReference( ref_unit ) );
  420. }
  421. if( lockedRef.CompareValue( ref_unit ) != 0 )
  422. continue;
  423. if( lockedRef.CompareLibName( ref_unit ) != 0 )
  424. continue;
  425. // Find the matching symbol
  426. for( unsigned jj = ii + 1; jj < m_flatList.size(); jj++ )
  427. {
  428. if( !lockedRef.IsSameInstance( m_flatList[jj] ) )
  429. continue;
  430. wxString ref_candidate = buildFullReference( ref_unit, lockedRef.m_unit );
  431. // propagate the new reference and unit selection to the "old" symbol,
  432. // if this new full reference is not already used (can happens when initial
  433. // multiunits symbols have duplicate references)
  434. if( inUseRefs.find( ref_candidate ) == inUseRefs.end() )
  435. {
  436. m_flatList[jj].m_numRef = ref_unit.m_numRef;
  437. m_flatList[jj].m_numRefStr = ref_unit.m_numRefStr;
  438. m_flatList[jj].m_isNew = false;
  439. m_flatList[jj].m_flag = 1;
  440. // lock this new full reference
  441. inUseRefs.insert( ref_candidate );
  442. break;
  443. }
  444. }
  445. }
  446. }
  447. else if( ref_unit.m_isNew )
  448. {
  449. // Reference belonging to multi-unit symbol that has not yet been annotated. We don't
  450. // know what group this might belong to, so just find the first unused reference for
  451. // this specific unit. The other units will be annotated in the following passes.
  452. std::vector<int> units = { ref_unit.GetUnit() };
  453. LastReferenceNumber = FindFirstUnusedReference( ref_unit, minRefId, units );
  454. ref_unit.m_numRef = LastReferenceNumber;
  455. ref_unit.m_numRefStr = ref_unit.formatRefStr( LastReferenceNumber );
  456. ref_unit.m_isNew = false;
  457. ref_unit.m_flag = 1;
  458. }
  459. }
  460. // Remove aAdditionalRefs references
  461. m_flatList.erase( m_flatList.begin() + originalSize, m_flatList.end() );
  462. wxASSERT( originalSize == GetCount() ); // Make sure we didn't make a mistake
  463. }
  464. int SCH_REFERENCE_LIST::CheckAnnotation( ANNOTATION_ERROR_HANDLER aHandler )
  465. {
  466. int error = 0;
  467. wxString tmp;
  468. wxString tmp2;
  469. wxString msg;
  470. SortByRefAndValue();
  471. // Split reference designators into name (prefix) and number: IC1 becomes IC, and 1.
  472. SplitReferences();
  473. // count not yet annotated items or annotation error.
  474. for( unsigned ii = 0; ii < m_flatList.size(); ii++ )
  475. {
  476. msg.Empty();
  477. tmp.Empty();
  478. if( m_flatList[ii].m_isNew ) // Not yet annotated
  479. {
  480. if( m_flatList[ii].m_numRef >= 0 )
  481. tmp << m_flatList[ii].m_numRefStr;
  482. else
  483. tmp = wxT( "?" );
  484. if( ( m_flatList[ii].m_unit > 0 ) && ( m_flatList[ii].m_unit < 0x7FFFFFFF )
  485. && m_flatList[ii].GetLibPart()->GetUnitCount() > 1 )
  486. {
  487. msg.Printf( _( "Item not annotated: %s%s (unit %d)" ),
  488. m_flatList[ii].GetRef(),
  489. tmp,
  490. m_flatList[ii].m_unit );
  491. }
  492. else
  493. {
  494. msg.Printf( _( "Item not annotated: %s%s" ), m_flatList[ii].GetRef(), tmp );
  495. }
  496. aHandler( ERCE_UNANNOTATED, msg, &m_flatList[ii], nullptr );
  497. error++;
  498. break;
  499. }
  500. // Error if unit number selected does not exist (greater than the number of units in
  501. // the symbol). This can happen if a symbol has changed in a library after a
  502. // previous annotation.
  503. if( std::max( m_flatList[ii].GetLibPart()->GetUnitCount(), 1 ) < m_flatList[ii].m_unit )
  504. {
  505. if( m_flatList[ii].m_numRef >= 0 )
  506. tmp << m_flatList[ii].m_numRefStr;
  507. else
  508. tmp = wxT( "?" );
  509. msg.Printf( _( "Error: symbol %s%s%s (unit %d) exceeds units defined (%d)" ),
  510. m_flatList[ii].GetRef(),
  511. tmp,
  512. m_flatList[ii].GetSymbol()->SubReference( m_flatList[ii].GetUnit() ),
  513. m_flatList[ii].m_unit,
  514. m_flatList[ii].GetLibPart()->GetUnitCount() );
  515. aHandler( ERCE_EXTRA_UNITS, msg, &m_flatList[ii], nullptr );
  516. error++;
  517. break;
  518. }
  519. }
  520. // count the duplicated elements (if all are annotated)
  521. int imax = m_flatList.size() - 1;
  522. for( int ii = 0; ii < imax; ii++ )
  523. {
  524. msg.Empty();
  525. tmp.Empty();
  526. tmp2.Empty();
  527. SCH_REFERENCE& first = m_flatList[ii];
  528. SCH_REFERENCE& second = m_flatList[ii + 1];
  529. if( ( first.CompareRef( second ) != 0 )
  530. || ( first.m_numRef != second.m_numRef ) )
  531. {
  532. continue;
  533. }
  534. // Same reference found. If same unit, error!
  535. if( first.m_unit == second.m_unit )
  536. {
  537. if( first.m_numRef >= 0 )
  538. tmp << first.m_numRefStr;
  539. else
  540. tmp = wxT( "?" );
  541. msg.Printf( _( "Duplicate items %s%s%s\n" ),
  542. first.GetRef(),
  543. tmp,
  544. first.GetLibPart()->GetUnitCount() > 1 ? first.GetSymbol()->SubReference( first.GetUnit() )
  545. : wxString( wxT( "" ) ) );
  546. aHandler( ERCE_DUPLICATE_REFERENCE, msg, &first, &m_flatList[ii+1] );
  547. error++;
  548. continue;
  549. }
  550. /* Test error if units are different but number of parts per package
  551. * too high (ex U3 ( 1 part) and we find U3B this is an error) */
  552. if( first.GetLibPart()->GetUnitCount() != second.GetLibPart()->GetUnitCount() )
  553. {
  554. if( first.m_numRef >= 0 )
  555. tmp << first.m_numRefStr;
  556. else
  557. tmp = wxT( "?" );
  558. if( second.m_numRef >= 0 )
  559. tmp2 << second.m_numRefStr;
  560. else
  561. tmp2 = wxT( "?" );
  562. msg.Printf( _( "Differing unit counts for item %s%s%s and %s%s%s\n" ),
  563. first.GetRef(),
  564. tmp,
  565. first.GetSymbol()->SubReference( first.GetUnit() ),
  566. second.GetRef(),
  567. tmp2,
  568. first.GetSymbol()->SubReference( second.GetUnit() ) );
  569. aHandler( ERCE_DUPLICATE_REFERENCE, msg, &first, &second );
  570. error++;
  571. continue;
  572. }
  573. // Error if values are different between units, for the same reference
  574. if( first.CompareValue( second ) != 0 )
  575. {
  576. msg.Printf( _( "Different values for %s%d%s (%s) and %s%d%s (%s)" ),
  577. first.GetRef(),
  578. first.m_numRef,
  579. first.GetSymbol()->SubReference( first.GetUnit() ),
  580. first.m_value,
  581. second.GetRef(),
  582. second.m_numRef,
  583. first.GetSymbol()->SubReference( second.GetUnit() ),
  584. second.m_value );
  585. aHandler( ERCE_DIFFERENT_UNIT_VALUE, msg, &first, &second );
  586. error++;
  587. }
  588. }
  589. return error;
  590. }
  591. SCH_REFERENCE::SCH_REFERENCE( SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSheetPath )
  592. {
  593. wxASSERT( aSymbol != nullptr );
  594. m_rootSymbol = aSymbol;
  595. m_unit = aSymbol->GetUnitSelection( &aSheetPath );
  596. m_footprint = aSymbol->GetFootprintFieldText( true, &aSheetPath, false );
  597. m_sheetPath = aSheetPath;
  598. m_isNew = false;
  599. m_flag = 0;
  600. m_symbolUuid = aSymbol->m_Uuid;
  601. m_symbolPos = aSymbol->GetPosition();
  602. m_sheetNum = 0;
  603. if( aSymbol->GetRef( &aSheetPath ).IsEmpty() )
  604. aSymbol->SetRef( &aSheetPath, wxT( "DefRef?" ) );
  605. wxString ref = aSymbol->GetRef( &aSheetPath );
  606. SetRef( ref );
  607. m_numRef = -1;
  608. if( aSymbol->GetValue( false, &aSheetPath, false ).IsEmpty() )
  609. aSymbol->SetValueFieldText( wxT( "~" ) );
  610. m_value = aSymbol->GetValue( false, &aSheetPath, false );
  611. }
  612. void SCH_REFERENCE::Annotate()
  613. {
  614. if( m_numRef < 0 )
  615. m_ref += '?';
  616. else
  617. m_ref = TO_UTF8( GetRef() << GetRefNumber() );
  618. m_rootSymbol->SetRef( &m_sheetPath, From_UTF8( m_ref.c_str() ) );
  619. m_rootSymbol->SetUnit( m_unit );
  620. m_rootSymbol->SetUnitSelection( &m_sheetPath, m_unit );
  621. }
  622. bool SCH_REFERENCE::AlwaysAnnotate() const
  623. {
  624. wxCHECK( m_rootSymbol && m_rootSymbol->GetLibSymbolRef()
  625. && !m_rootSymbol->GetRef( &m_sheetPath ).IsEmpty(), false );
  626. return m_rootSymbol->GetLibSymbolRef()->IsPower()
  627. || m_rootSymbol->GetRef( &m_sheetPath )[0] == wxUniChar( '#' );
  628. }
  629. void SCH_REFERENCE::Split()
  630. {
  631. std::string refText = GetRefStr();
  632. m_numRef = -1;
  633. m_numRefStr.Clear();
  634. int ll = refText.length() - 1;
  635. if( refText[ll] == '?' )
  636. {
  637. m_isNew = true;
  638. refText.erase( ll ); // delete last char
  639. SetRefStr( refText );
  640. }
  641. else if( isdigit( refText[ll] ) == 0 )
  642. {
  643. m_isNew = true;
  644. }
  645. else
  646. {
  647. while( ll >= 0 )
  648. {
  649. if( (refText[ll] <= ' ' ) || isdigit( refText[ll] ) )
  650. {
  651. ll--;
  652. }
  653. else
  654. {
  655. if( isdigit( refText[ll + 1] ) )
  656. {
  657. // null terminated C string into cp
  658. const char* cp = refText.c_str() + ll + 1;
  659. m_numRef = atoi( cp );
  660. }
  661. m_numRefStr = std::string( refText, ll + 1 );
  662. refText.erase( ll + 1 );
  663. break;
  664. }
  665. }
  666. SetRefStr( refText );
  667. }
  668. }
  669. bool SCH_REFERENCE::IsSplitNeeded()
  670. {
  671. std::string refText = GetRefStr();
  672. if( refText.empty() )
  673. return false;
  674. int ll = refText.length() - 1;
  675. return ( refText[ll] == '?' ) || isdigit( refText[ll] );
  676. }
  677. wxString SCH_REFERENCE_LIST::Shorthand( std::vector<SCH_REFERENCE> aList,
  678. const wxString& refDelimiter,
  679. const wxString& refRangeDelimiter )
  680. {
  681. wxString retVal;
  682. size_t i = 0;
  683. while( i < aList.size() )
  684. {
  685. wxString ref = aList[ i ].GetRef();
  686. int numRef = aList[ i ].m_numRef;
  687. size_t range = 1;
  688. while( i + range < aList.size()
  689. && aList[ i + range ].GetRef() == ref
  690. && aList[ i + range ].m_numRef == int( numRef + range ) )
  691. {
  692. range++;
  693. if( range == 2 && refRangeDelimiter.IsEmpty() )
  694. break;
  695. }
  696. if( !retVal.IsEmpty() )
  697. retVal << refDelimiter;
  698. if( range == 1 )
  699. {
  700. retVal << ref << aList[ i ].GetRefNumber();
  701. }
  702. else if( range == 2 || refRangeDelimiter.IsEmpty() )
  703. {
  704. retVal << ref << aList[ i ].GetRefNumber();
  705. retVal << refDelimiter;
  706. retVal << ref << aList[ i + 1 ].GetRefNumber();
  707. }
  708. else
  709. {
  710. retVal << ref << aList[ i ].GetRefNumber();
  711. retVal << refRangeDelimiter;
  712. retVal << ref << aList[ i + ( range - 1 ) ].GetRefNumber();
  713. }
  714. i+= range;
  715. }
  716. return retVal;
  717. }
  718. wxString SCH_REFERENCE::formatRefStr( int aNumber ) const
  719. {
  720. // To avoid a risk of duplicate, for power symbols the ref number is 0nnn instead of nnn.
  721. // Just because sometimes only power symbols are annotated
  722. if( GetSymbol() && GetLibPart() && GetLibPart()->IsPower() )
  723. return wxString::Format( "0%d", aNumber );
  724. return wxString::Format( "%d", aNumber );
  725. }
  726. #if defined( DEBUG )
  727. void SCH_REFERENCE_LIST::Show( const char* aPrefix )
  728. {
  729. printf( "%s\n", aPrefix );
  730. for( unsigned i = 0; i < m_flatList.size(); ++i )
  731. {
  732. SCH_REFERENCE& schref = m_flatList[i];
  733. printf( " [%-2d] ref:%-8s num:%-3d lib_part:%s\n", i, schref.m_ref.ToStdString().c_str(),
  734. schref.m_numRef, TO_UTF8( schref.GetLibPart()->GetName() ) );
  735. }
  736. }
  737. #endif