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.

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