KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_checker.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <vector>
21#include <eda_draw_frame.h>
22#include <lib_symbol.h>
23#include <sch_shape.h>
24#include <macros.h>
25
26// helper function to sort pins by pin num
27static bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst );
28
29
30static void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
31 UNITS_PROVIDER* aUnitsProvider );
32
33
34void CheckDuplicatePins( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages, UNITS_PROVIDER* aUnitsProvider )
35{
36 wxString msg;
37 std::vector<SCH_PIN*> pinList = aSymbol->GetGraphicalPins( 0, 0 );
38
39 std::vector<LIB_SYMBOL::LOGICAL_PIN> logicalPins;
40 logicalPins.reserve( pinList.size() );
41
42 for( SCH_PIN* pin : pinList )
43 {
44 bool valid = false;
45 std::vector<wxString> numbers = pin->GetStackedPinNumbers( &valid );
46
47 if( !valid || numbers.empty() )
48 {
49 logicalPins.push_back( { pin, pin->GetNumber() } );
50 continue;
51 }
52
53 for( const wxString& number : numbers )
54 logicalPins.push_back( { pin, number } );
55 }
56
57 sort( logicalPins.begin(), logicalPins.end(),
58 []( const LIB_SYMBOL::LOGICAL_PIN& lhs, const LIB_SYMBOL::LOGICAL_PIN& rhs )
59 {
60 int result = lhs.number.Cmp( rhs.number );
61
62 if( result == 0 )
63 result = lhs.pin->GetBodyStyle() - rhs.pin->GetBodyStyle();
64
65 if( result == 0 )
66 result = lhs.pin->GetUnit() - rhs.pin->GetUnit();
67
68 if( result == 0 && lhs.pin != rhs.pin )
69 return lhs.pin < rhs.pin;
70
71 return result < 0;
72 } );
73
74 for( unsigned ii = 1; ii < logicalPins.size(); ii++ )
75 {
76 LIB_SYMBOL::LOGICAL_PIN& prev = logicalPins[ii - 1];
77 LIB_SYMBOL::LOGICAL_PIN& next = logicalPins[ii];
78
79 if( prev.number != next.number )
80 continue;
81
82 if( prev.pin == next.pin )
83 continue;
84
85 // Pins are not duplicated only if they are in different body styles
86 if( prev.pin->GetBodyStyle() != ALL_BODY_STYLES && next.pin->GetBodyStyle() != ALL_BODY_STYLES )
87 {
88 if( prev.pin->GetBodyStyle() != next.pin->GetBodyStyle() )
89 continue;
90 }
91
92 wxString pinName;
93 wxString nextName;
94
95 if( !prev.pin->GetName().IsEmpty() )
96 pinName = " '" + prev.pin->GetName() + "'";
97
98 if( !next.pin->GetName().IsEmpty() )
99 nextName = " '" + next.pin->GetName() + "'";
100
101 auto formatNumberForMessage = []( const SCH_PIN* pin, const wxString& logicalNumber )
102 {
103 wxString shown = pin->GetNumber();
104
105 if( shown == logicalNumber )
106 return logicalNumber;
107
108 return wxString::Format( wxT( "%s (%s)" ), logicalNumber, shown );
109 };
110
111 wxString prevNumber = formatNumberForMessage( prev.pin, prev.number );
112 wxString nextNumber = formatNumberForMessage( next.pin, next.number );
113
114 if( aSymbol->IsMultiBodyStyle() && next.pin->GetBodyStyle() )
115 {
116 if( prev.pin->GetUnit() == ALL_UNITS || next.pin->GetUnit() == ALL_UNITS )
117 {
118 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
119 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
120 " in %s body style." ),
121 nextNumber,
122 nextName,
123 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
124 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
125 prevNumber,
126 prev.pin->GetName(),
127 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
128 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
129 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
130 }
131 else
132 {
133 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
134 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
135 " in units %s and %s of %s body style." ),
136 nextNumber,
137 nextName,
138 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
139 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
140 prevNumber,
141 pinName,
142 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
143 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
144 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
145 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ),
146 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
147 }
148 }
149 else
150 {
151 if( prev.pin->GetUnit() == ALL_UNITS || next.pin->GetUnit() == ALL_UNITS )
152 {
153 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
154 " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
155 nextNumber,
156 nextName,
157 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
158 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
159 prevNumber,
160 pinName,
161 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
162 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ) );
163 }
164 else
165 {
166 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
167 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
168 " in units %s and %s." ),
169 nextNumber,
170 nextName,
171 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
172 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
173 prevNumber,
174 pinName,
175 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
176 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
177 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
178 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ) );
179 }
180 }
181
182 msg += wxT( "<br><br>" );
183 aMessages.push_back( msg );
184 }
185}
186
187
203void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
204 int aGridForPins, UNITS_PROVIDER* aUnitsProvider )
205{
206 if( !aSymbol )
207 return;
208
209 wxString msg;
210
211 // Test reference prefix validity:
212 // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
213 // but it is acceptable if the symbol is saved to a schematic.
214 wxString reference_base = aSymbol->GetReferenceField().GetText();
215
216 if( reference_base.IsEmpty() )
217 {
218 aMessages.push_back( _( "<b>Warning: reference is empty</b><br><br>" ) );
219 }
220 else
221 {
222 wxString illegal_end( wxT( "0123456789?" ) );
223 wxUniChar last_char = reference_base.Last();
224
225 if( illegal_end.Find( last_char ) != wxNOT_FOUND )
226 {
227 msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
228 " issues if saved in a symbol library" ),
229 illegal_end );
230 msg += wxT( "<br><br>" );
231 aMessages.push_back( msg );
232 }
233 }
234
235 CheckDuplicatePins( aSymbol, aMessages, aUnitsProvider );
236
237 std::vector<SCH_PIN*> pinList = aSymbol->GetGraphicalPins( 0, 0 );
238 sort( pinList.begin(), pinList.end(), sort_by_pin_number );
239
240 // The minimal grid size allowed to place a pin is 25 mils
241 // the best grid size is 50 mils, but 25 mils is still usable
242 // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
243 // the wires must be on the 50 mils grid
244 // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
245 const int min_grid_size = schIUScale.MilsToIU( 25 );
246 const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
247
248 // Test for a valid power aSymbol.
249 // A valid power aSymbol has only one unit, no alternate body styles and one pin.
250 // And this pin should be PT_POWER_IN (invisible to be automatically connected)
251 // or PT_POWER_OUT for a power flag
252 if( aSymbol->IsPower() )
253 {
254 if( aSymbol->GetUnitCount() != 1 )
255 {
256 msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
257 aMessages.push_back( msg );
258 }
259
260 if( pinList.size() == 0 )
261 {
262 msg.Printf( _( "<b>A Power Symbol should have exactly one pin</b><br><br>" ) );
263 aMessages.push_back( msg );
264 }
265 else
266 {
267 if( pinList.size() > 1 )
268 {
269 msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
270 aMessages.push_back( msg );
271 }
272
273 SCH_PIN* pin = pinList[0];
274
275 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
276 && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
277 {
278 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
279 "Only an input or output power pin has meaning<br><br>" ) );
280 aMessages.push_back( msg );
281 }
282
283 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !pin->IsVisible() )
284 {
285 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
286 "Invisible input power pins are no longer required<br><br>" ) );
287 aMessages.push_back( msg );
288 }
289 }
290 }
291
292
293 for( SCH_PIN* pin : pinList )
294 {
295 wxString pinName = pin->GetName();
296
297 if( pinName.IsEmpty() || pinName == "~" )
298 pinName = "";
299 else
300 pinName = "'" + pinName + "'";
301
302 if( !aSymbol->IsGlobalPower()
303 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
304 && !pin->IsVisible() )
305 {
306 // hidden power pin
307 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
308 {
309 if( aSymbol->GetUnitCount() <= 1 )
310 {
311 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
312 " in %s body style." ),
313 pin->GetNumber(),
314 pinName,
315 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
316 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
317 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
318 }
319 else
320 {
321 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
322 " in unit %c of %s body style." ),
323 pin->GetNumber(),
324 pinName,
325 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
326 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
327 'A' + pin->GetUnit() - 1,
328 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
329 }
330 }
331 else
332 {
333 if( aSymbol->GetUnitCount() <= 1 )
334 {
335 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>"
336 "(%s, %s)</b>." ),
337 pin->GetNumber(),
338 pinName,
339 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
340 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
341 }
342 else
343 {
344 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
345 " in unit %c." ),
346 pin->GetNumber(),
347 pinName,
348 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
349 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
350 'A' + pin->GetUnit() - 1 );
351 }
352 }
353
354 msg += wxT( "<br>" );
355 msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
356 msg += wxT( "<br><br>" );
357 aMessages.push_back( msg );
358 }
359
360 if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
361 || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
362 {
363 // pin is off grid
364 msg.Empty();
365
366 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
367 {
368 if( aSymbol->GetUnitCount() <= 1 )
369 {
370 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
371 " of %s body style." ),
372 pin->GetNumber(),
373 pinName,
374 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
375 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
376 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
377 }
378 else
379 {
380 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
381 " in unit %c of %s body style." ),
382 pin->GetNumber(),
383 pinName,
384 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
385 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
386 'A' + pin->GetUnit() - 1,
387 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
388 }
389 }
390 else
391 {
392 if( aSymbol->GetUnitCount() <= 1 )
393 {
394 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
395 pin->GetNumber(),
396 pinName,
397 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
398 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
399 }
400 else
401 {
402 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
403 " in unit %c." ),
404 pin->GetNumber(),
405 pinName,
406 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
407 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
408 'A' + pin->GetUnit() - 1 );
409 }
410 }
411
412 msg += wxT( "<br><br>" );
413 aMessages.push_back( msg );
414 }
415 }
416
417 CheckLibSymbolGraphics( aSymbol, aMessages, aUnitsProvider );
418}
419
420
421void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
422 UNITS_PROVIDER* aUnitsProvider )
423{
424 if( !aSymbol )
425 return;
426
427 wxString msg;
428
429 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
430 {
431 if( item.Type() != SCH_SHAPE_T )
432 continue;
433
434 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( &item );
435
436 switch( shape->GetShape() )
437 {
438 case SHAPE_T::ARC:
439 break;
440
441 case SHAPE_T::CIRCLE:
442 if( shape->GetRadius() <= 0 )
443 {
444 msg.Printf( _( "<b>Graphic circle has radius = 0</b> at location "
445 "<b>(%s, %s)</b>." ),
446 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
447 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
448 msg += wxT( "<br>" );
449 aMessages.push_back( msg );
450 }
451 break;
452
454 if( shape->GetPosition() == shape->GetEnd() )
455 {
456 msg.Printf( _( "<b>Graphic rectangle has size 0</b> at location <b>(%s, %s)</b>." ),
457 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
458 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
459 msg += wxT( "<br>" );
460 aMessages.push_back( msg );
461 }
462 break;
463
464 case SHAPE_T::POLY:
465 break;
466
467 case SHAPE_T::BEZIER:
468 break;
469
470 case SHAPE_T::ELLIPSE:
472 if( shape->GetEllipseMajorRadius() <= 0 || shape->GetEllipseMinorRadius() <= 0 )
473 {
474 msg.Printf( _( "<b>Graphic ellipse has null or negative radii</b> at location "
475 "<b>(%s, %s)</b>." ),
476 aUnitsProvider->MessageTextFromValue( shape->GetPosition().x ),
477 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
478 msg += wxT( "<br>" );
479 aMessages.push_back( msg );
480 }
481
482 if( shape->GetShape() == SHAPE_T::ELLIPSE_ARC
483 && shape->GetEllipseStartAngle() == shape->GetEllipseEndAngle() )
484 {
485 msg.Printf( _( "<b>Graphic elliptical arc has zero sweep</b> at location "
486 "<b>(%s, %s)</b>." ),
487 aUnitsProvider->MessageTextFromValue( shape->GetPosition().x ),
488 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
489 msg += wxT( "<br>" );
490 aMessages.push_back( msg );
491 }
492 break;
493
494
495 default:
497 }
498 }
499}
500
501
502bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst )
503{
504 // Use number as primary key
505 int test = ref->GetNumber().Cmp( tst->GetNumber() );
506
507 // Use DeMorgan variant as secondary key
508 if( test == 0 )
509 test = ref->GetBodyStyle() - tst->GetBodyStyle();
510
511 // Use unit as tertiary key
512 if( test == 0 )
513 test = ref->GetUnit() - tst->GetUnit();
514
515 return test < 0;
516}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
int GetEllipseMinorRadius() const
Definition eda_shape.h:395
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:423
int GetEllipseMajorRadius() const
Definition eda_shape.h:386
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:175
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:325
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:414
wxString SHAPE_T_asString() const
Define a library symbol object.
Definition lib_symbol.h:114
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:902
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
bool IsGlobalPower() const override
wxString GetBodyStyleDescription(int aBodyStyle, bool aLabel) const override
int GetUnitCount() const override
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the user-defined display name for aUnit for symbols with units.
SCH_FIELD & GetReferenceField()
Definition lib_symbol.h:451
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
int GetBodyStyle() const
Definition sch_item.h:253
int GetUnit() const
Definition sch_item.h:243
const wxString & GetName() const
Definition sch_pin.cpp:527
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:378
const wxString & GetNumber() const
Definition sch_pin.h:144
VECTOR2I GetPosition() const override
Definition sch_shape.h:86
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
#define _(s)
@ ELLIPSE
Definition eda_shape.h:62
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:43
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
CITER next(CITER it)
Definition ptree.cpp:120
Logical pins: Return expanded logical pins based on stacked-pin notation.
Definition lib_symbol.h:715
wxString number
expanded logical pin number
Definition lib_symbol.h:717
const SCH_PIN * pin
pointer to the base graphical pin
Definition lib_symbol.h:716
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
static bool sort_by_pin_number(const SCH_PIN *ref, const SCH_PIN *tst)
void CheckDuplicatePins(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, UNITS_PROVIDER *aUnitsProvider)
void CheckLibSymbol(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, int aGridForPins, UNITS_PROVIDER *aUnitsProvider)
Check a library symbol to find incorrect settings.
static void CheckLibSymbolGraphics(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, UNITS_PROVIDER *aUnitsProvider)
KIBIS_PIN * pin
@ SCH_SHAPE_T
Definition typeinfo.h:145