1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
6 * Copyright (C) 1992-2021 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 #include "connection_graph.h"
27 #include <common.h> // for ExpandEnvVarSubstitutions
28 #include <erc.h>
29 #include <string_utils.h>
30 #include <lib_pin.h>
31 #include <sch_edit_frame.h>
32 #include <sch_marker.h>
33 #include <sch_reference_list.h>
34 #include <sch_sheet.h>
35 #include <sch_sheet_pin.h>
36 #include <schematic.h>
37 #include <drawing_sheet/ds_draw_item.h>
38 #include <drawing_sheet/ds_proxy_view_item.h>
39 #include <wx/ffile.h>
40
41
42 /* ERC tests :
43 * 1 - conflicts between connected pins ( example: 2 connected outputs )
44 * 2 - minimal connections requirements ( 1 input *must* be connected to an
45 * output, or a passive pin )
46 */
47
48 /*
49 * Minimal ERC requirements:
50 * All pins *must* be connected (except ELECTRICAL_PINTYPE::PT_NC).
51 * When a pin is not connected in schematic, the user must place a "non
52 * connected" symbol to this pin.
53 * This ensures a forgotten connection will be detected.
54 */
55
56 // Messages for matrix rows:
57 const wxString CommentERC_H[] =
58 {
59 _( "Input Pin" ),
60 _( "Output Pin" ),
61 _( "Bidirectional Pin" ),
62 _( "Tri-State Pin" ),
63 _( "Passive Pin" ),
64 _( "Free Pin" ),
65 _( "Unspecified Pin" ),
66 _( "Power Input Pin" ),
67 _( "Power Output Pin" ),
68 _( "Open Collector" ),
69 _( "Open Emitter" ),
70 _( "No Connection" )
71 };
72
73 // Messages for matrix columns
74 const wxString CommentERC_V[] =
75 {
76 _( "Input Pin" ),
77 _( "Output Pin" ),
78 _( "Bidirectional Pin" ),
79 _( "Tri-State Pin" ),
80 _( "Passive Pin" ),
81 _( "Free Pin" ),
82 _( "Unspecified Pin" ),
83 _( "Power Input Pin" ),
84 _( "Power Output Pin" ),
85 _( "Open Collector" ),
86 _( "Open Emitter" ),
87 _( "No Connection" )
88 };
89
90
91 // List of pin types that are considered drivers for usual input pins
92 // i.e. pin type = ELECTRICAL_PINTYPE::PT_INPUT, but not PT_POWER_IN
93 // that need only a PT_POWER_OUT pin type to be driven
94 const std::set<ELECTRICAL_PINTYPE> DrivingPinTypes =
95 {
96 ELECTRICAL_PINTYPE::PT_OUTPUT,
97 ELECTRICAL_PINTYPE::PT_POWER_OUT,
98 ELECTRICAL_PINTYPE::PT_PASSIVE,
99 ELECTRICAL_PINTYPE::PT_TRISTATE,
100 ELECTRICAL_PINTYPE::PT_BIDI
101 };
102
103 // List of pin types that are considered drivers for power pins
104 // In fact only a ELECTRICAL_PINTYPE::PT_POWER_OUT pin type can drive
105 // power input pins
106 const std::set<ELECTRICAL_PINTYPE> DrivingPowerPinTypes =
107 {
108 ELECTRICAL_PINTYPE::PT_POWER_OUT
109 };
110
111 // List of pin types that require a driver elsewhere on the net
112 const std::set<ELECTRICAL_PINTYPE> DrivenPinTypes =
113 {
114 ELECTRICAL_PINTYPE::PT_INPUT,
115 ELECTRICAL_PINTYPE::PT_POWER_IN
116 };
117
TestDuplicateSheetNames(bool aCreateMarker)118 int ERC_TESTER::TestDuplicateSheetNames( bool aCreateMarker )
119 {
120 SCH_SCREEN* screen;
121 int err_count = 0;
122
123 SCH_SCREENS screenList( m_schematic->Root() );
124
125 for( screen = screenList.GetFirst(); screen != nullptr; screen = screenList.GetNext() )
126 {
127 std::vector<SCH_SHEET*> list;
128
129 for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
130 list.push_back( static_cast<SCH_SHEET*>( item ) );
131
132 for( size_t i = 0; i < list.size(); i++ )
133 {
134 SCH_SHEET* sheet = list[i];
135
136 for( size_t j = i + 1; j < list.size(); j++ )
137 {
138 SCH_SHEET* test_item = list[j];
139
140 // We have found a second sheet: compare names
141 // we are using case insensitive comparison to avoid mistakes between
142 // similar names like Mysheet and mysheet
143 if( sheet->GetName().CmpNoCase( test_item->GetName() ) == 0 )
144 {
145 if( aCreateMarker )
146 {
147 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DUPLICATE_SHEET_NAME );
148 ercItem->SetItems( sheet, test_item );
149
150 SCH_MARKER* marker = new SCH_MARKER( ercItem, sheet->GetPosition() );
151 screen->Append( marker );
152 }
153
154 err_count++;
155 }
156 }
157 }
158 }
159
160 return err_count;
161 }
162
163
TestTextVars(DS_PROXY_VIEW_ITEM * aDrawingSheet)164 void ERC_TESTER::TestTextVars( DS_PROXY_VIEW_ITEM* aDrawingSheet )
165 {
166 DS_DRAW_ITEM_LIST wsItems;
167
168 auto unresolved = [this]( wxString str )
169 {
170 str = ExpandEnvVarSubstitutions( str, &m_schematic->Prj() );
171 return str.Matches( wxT( "*${*}*" ) );
172 };
173
174 if( aDrawingSheet )
175 {
176 wsItems.SetMilsToIUfactor( IU_PER_MILS );
177 wsItems.SetPageNumber( "1" );
178 wsItems.SetSheetCount( 1 );
179 wsItems.SetFileName( "dummyFilename" );
180 wsItems.SetSheetName( "dummySheet" );
181 wsItems.SetSheetLayer( "dummyLayer" );
182 wsItems.SetProject( &m_schematic->Prj() );
183 wsItems.BuildDrawItemsList( aDrawingSheet->GetPageInfo(), aDrawingSheet->GetTitleBlock());
184 }
185
186 SCH_SHEET_PATH savedCurrentSheet = m_schematic->CurrentSheet();
187 SCH_SHEET_LIST sheets = m_schematic->GetSheets();
188
189 for( SCH_SHEET_PATH& sheet : sheets )
190 {
191 m_schematic->SetCurrentSheet( sheet );
192 SCH_SCREEN* screen = sheet.LastScreen();
193
194 for( SCH_ITEM* item : screen->Items().OfType( SCH_LOCATE_ANY_T ) )
195 {
196 if( item->Type() == SCH_SYMBOL_T )
197 {
198 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
199
200 for( SCH_FIELD& field : symbol->GetFields() )
201 {
202 if( unresolved( field.GetShownText() ) )
203 {
204 wxPoint pos = field.GetPosition() - symbol->GetPosition();
205 pos = symbol->GetTransform().TransformCoordinate( pos );
206 pos += symbol->GetPosition();
207
208 std::shared_ptr<ERC_ITEM> ercItem =
209 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
210 ercItem->SetItems( &field );
211
212 SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
213 screen->Append( marker );
214 }
215 }
216 }
217 else if( item->Type() == SCH_SHEET_T )
218 {
219 SCH_SHEET* subSheet = static_cast<SCH_SHEET*>( item );
220
221 for( SCH_FIELD& field : subSheet->GetFields() )
222 {
223 if( unresolved( field.GetShownText() ) )
224 {
225 std::shared_ptr<ERC_ITEM> ercItem =
226 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
227 ercItem->SetItems( &field );
228
229 SCH_MARKER* marker = new SCH_MARKER( ercItem, field.GetPosition() );
230 screen->Append( marker );
231 }
232 }
233
234 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
235 {
236 if( pin->GetShownText().Matches( wxT( "*${*}*" ) ) )
237 {
238 std::shared_ptr<ERC_ITEM> ercItem =
239 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
240 ercItem->SetItems( pin );
241
242 SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetPosition() );
243 screen->Append( marker );
244 }
245 }
246 }
247 else if( SCH_TEXT* text = dynamic_cast<SCH_TEXT*>( item ) )
248 {
249 if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
250 {
251 std::shared_ptr<ERC_ITEM> ercItem =
252 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
253 ercItem->SetItems( text );
254
255 SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
256 screen->Append( marker );
257 }
258 }
259 }
260
261 for( DS_DRAW_ITEM_BASE* item = wsItems.GetFirst(); item; item = wsItems.GetNext() )
262 {
263 if( DS_DRAW_ITEM_TEXT* text = dynamic_cast<DS_DRAW_ITEM_TEXT*>( item ) )
264 {
265 if( text->GetShownText().Matches( wxT( "*${*}*" ) ) )
266 {
267 std::shared_ptr<ERC_ITEM> ercItem =
268 ERC_ITEM::Create( ERCE_UNRESOLVED_VARIABLE );
269 ercItem->SetErrorMessage( _( "Unresolved text variable in drawing sheet." ) );
270
271 SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
272 screen->Append( marker );
273 }
274 }
275 }
276 }
277
278 m_schematic->SetCurrentSheet( savedCurrentSheet );
279 }
280
281
TestConflictingBusAliases()282 int ERC_TESTER::TestConflictingBusAliases()
283 {
284 wxString msg;
285 int err_count = 0;
286
287 SCH_SCREENS screens( m_schematic->Root() );
288 std::vector< std::shared_ptr<BUS_ALIAS> > aliases;
289
290 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
291 {
292 std::unordered_set< std::shared_ptr<BUS_ALIAS> > screen_aliases = screen->GetBusAliases();
293
294 for( const std::shared_ptr<BUS_ALIAS>& alias : screen_aliases )
295 {
296 for( const std::shared_ptr<BUS_ALIAS>& test : aliases )
297 {
298 if( alias->GetName() == test->GetName() && alias->Members() != test->Members() )
299 {
300 msg.Printf( _( "Bus alias %s has conflicting definitions on %s and %s" ),
301 alias->GetName(),
302 alias->GetParent()->GetFileName(),
303 test->GetParent()->GetFileName() );
304
305 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ALIAS_CONFLICT );
306 ercItem->SetErrorMessage( msg );
307
308 SCH_MARKER* marker = new SCH_MARKER( ercItem, wxPoint() );
309 test->GetParent()->Append( marker );
310
311 ++err_count;
312 }
313 }
314 }
315
316 aliases.insert( aliases.end(), screen_aliases.begin(), screen_aliases.end() );
317 }
318
319 return err_count;
320 }
321
322
TestMultiunitFootprints()323 int ERC_TESTER::TestMultiunitFootprints()
324 {
325 SCH_SHEET_LIST sheets = m_schematic->GetSheets();
326
327 int errors = 0;
328 std::map<wxString, LIB_ID> footprints;
329 SCH_MULTI_UNIT_REFERENCE_MAP refMap;
330 sheets.GetMultiUnitSymbols( refMap, true );
331
332 for( std::pair<const wxString, SCH_REFERENCE_LIST>& symbol : refMap )
333 {
334 SCH_REFERENCE_LIST& refList = symbol.second;
335
336 if( refList.GetCount() == 0 )
337 {
338 wxFAIL; // it should not happen
339 continue;
340 }
341
342 // Reference footprint
343 SCH_SYMBOL* unit = nullptr;
344 wxString unitName;
345 wxString unitFP;
346
347 for( unsigned i = 0; i < refList.GetCount(); ++i )
348 {
349 SCH_SHEET_PATH sheetPath = refList.GetItem( i ).GetSheetPath();
350 unitFP = refList.GetItem( i ).GetFootprint();
351
352 if( !unitFP.IsEmpty() )
353 {
354 unit = refList.GetItem( i ).GetSymbol();
355 unitName = unit->GetRef( &sheetPath, true );
356 break;
357 }
358 }
359
360 for( unsigned i = 0; i < refList.GetCount(); ++i )
361 {
362 SCH_REFERENCE& secondRef = refList.GetItem( i );
363 SCH_SYMBOL* secondUnit = secondRef.GetSymbol();
364 wxString secondName = secondUnit->GetRef( &secondRef.GetSheetPath(), true );
365 const wxString secondFp = secondRef.GetFootprint();
366 wxString msg;
367
368 if( unit && !secondFp.IsEmpty() && unitFP != secondFp )
369 {
370 msg.Printf( _( "Different footprints assigned to %s and %s" ),
371 unitName, secondName );
372
373 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_FP );
374 ercItem->SetErrorMessage( msg );
375 ercItem->SetItems( unit, secondUnit );
376
377 SCH_MARKER* marker = new SCH_MARKER( ercItem, secondUnit->GetPosition() );
378 secondRef.GetSheetPath().LastScreen()->Append( marker );
379
380 ++errors;
381 }
382 }
383 }
384
385 return errors;
386 }
387
388
TestNoConnectPins()389 int ERC_TESTER::TestNoConnectPins()
390 {
391 int err_count = 0;
392
393 for( const SCH_SHEET_PATH& sheet : m_schematic->GetSheets() )
394 {
395 std::map<wxPoint, std::vector<SCH_PIN*>> pinMap;
396
397 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
398 {
399 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
400
401 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
402 {
403 if( pin->GetLibPin()->GetType() == ELECTRICAL_PINTYPE::PT_NC )
404 pinMap[pin->GetPosition()].emplace_back( pin );
405 }
406 }
407
408 for( auto& pair : pinMap )
409 {
410 if( pair.second.size() > 1 )
411 {
412 err_count++;
413
414 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
415
416 ercItem->SetItems( pair.second[0], pair.second[1],
417 pair.second.size() > 2 ? pair.second[2] : nullptr,
418 pair.second.size() > 3 ? pair.second[3] : nullptr );
419 ercItem->SetErrorMessage( _( "Pins with \"no connection\" type are connected" ) );
420
421 SCH_MARKER* marker = new SCH_MARKER( ercItem, pair.first );
422 sheet.LastScreen()->Append( marker );
423 }
424 }
425 }
426
427 return err_count;
428 }
429
430
TestPinToPin()431 int ERC_TESTER::TestPinToPin()
432 {
433 ERC_SETTINGS& settings = m_schematic->ErcSettings();
434 const NET_MAP& nets = m_schematic->ConnectionGraph()->GetNetMap();
435
436 int errors = 0;
437
438 for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
439 {
440 std::vector<SCH_PIN*> pins;
441 std::unordered_map<EDA_ITEM*, SCH_SCREEN*> pinToScreenMap;
442
443 for( CONNECTION_SUBGRAPH* subgraph: net.second )
444 {
445 for( EDA_ITEM* item : subgraph->m_items )
446 {
447 if( item->Type() == SCH_PIN_T )
448 {
449 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
450 pinToScreenMap[item] = subgraph->m_sheet.LastScreen();
451 }
452 }
453 }
454
455 // Single-pin nets are handled elsewhere
456 if( pins.size() < 2 )
457 continue;
458
459 std::set<std::pair<SCH_PIN*, SCH_PIN*>> tested;
460
461 SCH_PIN* needsDriver = nullptr;
462 bool hasDriver = false;
463
464 // We need different drivers for power nets and normal nets.
465 // A power net has at least one pin having the ELECTRICAL_PINTYPE::PT_POWER_IN
466 // and power nets can be driven only by ELECTRICAL_PINTYPE::PT_POWER_OUT pins
467 bool ispowerNet = false;
468
469 for( SCH_PIN* refPin : pins )
470 {
471 if( refPin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
472 {
473 ispowerNet = true;
474 break;
475 }
476 }
477
478 for( SCH_PIN* refPin : pins )
479 {
480 ELECTRICAL_PINTYPE refType = refPin->GetType();
481
482 if( DrivenPinTypes.count( refType ) )
483 {
484 // needsDriver will be the pin shown in the error report eventually, so try to
485 // upgrade to a "better" pin if possible: something visible and only a power symbol
486 // if this net needs a power driver
487 if( !needsDriver ||
488 ( !needsDriver->IsVisible() && refPin->IsVisible() ) ||
489 ( ispowerNet != ( needsDriver->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN ) &&
490 ispowerNet == ( refType == ELECTRICAL_PINTYPE::PT_POWER_IN ) ) )
491 {
492 needsDriver = refPin;
493 }
494 }
495
496 if( ispowerNet )
497 hasDriver |= ( DrivingPowerPinTypes.count( refType ) != 0 );
498 else
499 hasDriver |= ( DrivingPinTypes.count( refType ) != 0 );
500
501 for( SCH_PIN* testPin : pins )
502 {
503 if( testPin == refPin )
504 continue;
505
506 std::pair<SCH_PIN*, SCH_PIN*> pair1 = std::make_pair( refPin, testPin );
507 std::pair<SCH_PIN*, SCH_PIN*> pair2 = std::make_pair( testPin, refPin );
508
509 if( tested.count( pair1 ) || tested.count( pair2 ) )
510 continue;
511
512 tested.insert( pair1 );
513 tested.insert( pair2 );
514
515 ELECTRICAL_PINTYPE testType = testPin->GetType();
516
517 if( ispowerNet )
518 hasDriver |= ( DrivingPowerPinTypes.count( testType ) != 0 );
519 else
520 hasDriver |= ( DrivingPinTypes.count( testType ) != 0 );
521
522 PIN_ERROR erc = settings.GetPinMapValue( refType, testType );
523
524 if( erc != PIN_ERROR::OK && settings.IsTestEnabled( ERCE_PIN_TO_PIN_WARNING ) )
525 {
526 std::shared_ptr<ERC_ITEM> ercItem =
527 ERC_ITEM::Create( erc == PIN_ERROR::WARNING ? ERCE_PIN_TO_PIN_WARNING :
528 ERCE_PIN_TO_PIN_ERROR );
529 ercItem->SetItems( refPin, testPin );
530 ercItem->SetIsSheetSpecific();
531
532 ercItem->SetErrorMessage(
533 wxString::Format( _( "Pins of type %s and %s are connected" ),
534 ElectricalPinTypeGetText( refType ),
535 ElectricalPinTypeGetText( testType ) ) );
536
537 SCH_MARKER* marker = new SCH_MARKER( ercItem,
538 refPin->GetTransformedPosition() );
539 pinToScreenMap[refPin]->Append( marker );
540 errors++;
541 }
542 }
543 }
544
545 if( needsDriver && !hasDriver )
546 {
547 int err_code = ispowerNet ? ERCE_POWERPIN_NOT_DRIVEN : ERCE_PIN_NOT_DRIVEN;
548
549 if( settings.IsTestEnabled( err_code ) )
550 {
551 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( err_code );
552
553 ercItem->SetItems( needsDriver );
554
555 SCH_MARKER* marker = new SCH_MARKER( ercItem,
556 needsDriver->GetTransformedPosition() );
557 pinToScreenMap[needsDriver]->Append( marker );
558 errors++;
559 }
560 }
561 }
562
563 return errors;
564 }
565
566
TestMultUnitPinConflicts()567 int ERC_TESTER::TestMultUnitPinConflicts()
568 {
569 const NET_MAP& nets = m_schematic->ConnectionGraph()->GetNetMap();
570
571 int errors = 0;
572
573 std::unordered_map<wxString, std::pair<wxString, SCH_PIN*>> pinToNetMap;
574
575 for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
576 {
577 const wxString& netName = net.first.first;
578 std::vector<SCH_PIN*> pins;
579
580 for( CONNECTION_SUBGRAPH* subgraph : net.second )
581 {
582 for( EDA_ITEM* item : subgraph->m_items )
583 {
584 if( item->Type() == SCH_PIN_T )
585 {
586 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
587
588 if( !pin->GetLibPin()->GetParent()->IsMulti() )
589 continue;
590
591 wxString name = pin->GetParentSymbol()->GetRef( &subgraph->m_sheet ) +
592 + ":" + pin->GetShownNumber();
593
594 if( !pinToNetMap.count( name ) )
595 {
596 pinToNetMap[name] = std::make_pair( netName, pin );
597 }
598 else if( pinToNetMap[name].first != netName )
599 {
600 std::shared_ptr<ERC_ITEM> ercItem =
601 ERC_ITEM::Create( ERCE_DIFFERENT_UNIT_NET );
602
603 ercItem->SetErrorMessage( wxString::Format(
604 _( "Pin %s is connected to both %s and %s" ),
605 pin->GetShownNumber(),
606 netName,
607 pinToNetMap[name].first ) );
608
609 ercItem->SetItems( pin, pinToNetMap[name].second );
610 ercItem->SetIsSheetSpecific();
611
612 SCH_MARKER* marker = new SCH_MARKER( ercItem,
613 pin->GetTransformedPosition() );
614 subgraph->m_sheet.LastScreen()->Append( marker );
615 errors += 1;
616 }
617 }
618 }
619 }
620 }
621
622 return errors;
623 }
624
625
TestSimilarLabels()626 int ERC_TESTER::TestSimilarLabels()
627 {
628 const NET_MAP& nets = m_schematic->ConnectionGraph()->GetNetMap();
629
630 int errors = 0;
631
632 std::unordered_map<wxString, SCH_TEXT*> labelMap;
633
634 for( const std::pair<NET_NAME_CODE, std::vector<CONNECTION_SUBGRAPH*>> net : nets )
635 {
636 std::vector<SCH_PIN*> pins;
637
638 for( CONNECTION_SUBGRAPH* subgraph : net.second )
639 {
640 for( EDA_ITEM* item : subgraph->m_items )
641 {
642 switch( item->Type() )
643 {
644 case SCH_LABEL_T:
645 case SCH_HIER_LABEL_T:
646 case SCH_GLOBAL_LABEL_T:
647 {
648 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
649
650 wxString normalized = text->GetShownText().Lower();
651
652 if( !labelMap.count( normalized ) )
653 {
654 labelMap[normalized] = text;
655 }
656 else if( labelMap.at( normalized )->GetShownText() != text->GetShownText() )
657 {
658 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SIMILAR_LABELS );
659 ercItem->SetItems( text, labelMap.at( normalized ) );
660
661 SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
662 subgraph->m_sheet.LastScreen()->Append( marker );
663 errors += 1;
664 }
665
666 break;
667 }
668
669 default:
670 break;
671 }
672 }
673 }
674 }
675
676 return errors;
677 }
678
679
TestLibSymbolIssues()680 int ERC_TESTER::TestLibSymbolIssues()
681 {
682 wxCHECK( m_schematic, 0 );
683
684 SYMBOL_LIB_TABLE* libTable = m_schematic->Prj().SchSymbolLibTable();
685 wxString msg;
686 int err_count = 0;
687
688 SCH_SCREENS screens( m_schematic->Root() );
689
690 for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() )
691 {
692 std::vector<SCH_MARKER*> markers;
693
694 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
695 {
696 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
697
698 wxCHECK2( symbol, continue );
699
700 LIB_SYMBOL* libSymbolInSchematic = symbol->GetLibSymbolRef().get();
701
702 wxCHECK2( libSymbolInSchematic, continue );
703
704 wxString libName = symbol->GetLibId().GetLibNickname();
705 LIB_TABLE_ROW* libTableRow = libTable->FindRow( libName, true );
706
707 if( !libTableRow )
708 {
709 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
710 ercItem->SetItems( symbol );
711 msg.Printf( _( "The current configuration does not include the library '%s'." ),
712 UnescapeString( libName ) );
713 ercItem->SetErrorMessage( msg );
714
715 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
716 continue;
717 }
718 else if( !libTable->HasLibrary( libName, true ) )
719 {
720 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
721 ercItem->SetItems( symbol );
722 msg.Printf( _( "The library '%s' is not enabled in the current configuration." ),
723 UnescapeString( libName ) );
724 ercItem->SetErrorMessage( msg );
725
726 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
727 continue;
728 }
729
730 wxString symbolName = symbol->GetLibId().GetLibItemName();
731 LIB_SYMBOL* libSymbol = SchGetLibSymbol( symbol->GetLibId(), libTable );
732
733 if( libSymbol == nullptr )
734 {
735 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
736 ercItem->SetItems( symbol );
737 msg.Printf( "Symbol '%s' not found in symbol library '%s'.",
738 UnescapeString( symbolName ),
739 UnescapeString( libName ) );
740 ercItem->SetErrorMessage( msg );
741
742 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
743 continue;
744 }
745
746 std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
747
748 if( *flattenedSymbol != *libSymbolInSchematic )
749 {
750 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LIB_SYMBOL_ISSUES );
751 ercItem->SetItems( symbol );
752 msg.Printf( "Symbol '%s' has been modified in library '%s'.",
753 UnescapeString( symbolName ),
754 UnescapeString( libName ) );
755 ercItem->SetErrorMessage( msg );
756
757 markers.emplace_back( new SCH_MARKER( ercItem, symbol->GetPosition() ) );
758 }
759 }
760
761 for( SCH_MARKER* marker : markers )
762 {
763 screen->Append( marker );
764 err_count += 1;
765 }
766 }
767
768 return err_count;
769 }
770