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 // (but GetBodyStyle() == 0 means common to all body styles)
87 if( prev.pin->GetBodyStyle() != 0 && next.pin->GetBodyStyle() != 0 )
88 {
89 if( prev.pin->GetBodyStyle() != next.pin->GetBodyStyle() )
90 continue;
91 }
92
93 wxString pinName;
94 wxString nextName;
95
96 if( !prev.pin->GetName().IsEmpty() )
97 pinName = " '" + prev.pin->GetName() + "'";
98
99 if( !next.pin->GetName().IsEmpty() )
100 nextName = " '" + next.pin->GetName() + "'";
101
102 auto formatNumberForMessage = []( const SCH_PIN* pin, const wxString& logicalNumber )
103 {
104 wxString shown = pin->GetNumber();
105
106 if( shown == logicalNumber )
107 return logicalNumber;
108
109 return wxString::Format( wxT( "%s (%s)" ), logicalNumber, shown );
110 };
111
112 wxString prevNumber = formatNumberForMessage( prev.pin, prev.number );
113 wxString nextNumber = formatNumberForMessage( next.pin, next.number );
114
115 if( aSymbol->IsMultiBodyStyle() && next.pin->GetBodyStyle() )
116 {
117 if( prev.pin->GetUnit() == 0 || next.pin->GetUnit() == 0 )
118 {
119 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
120 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
121 " in %s body style." ),
122 nextNumber,
123 nextName,
124 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
125 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
126 prevNumber,
127 prev.pin->GetName(),
128 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
129 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
130 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
131 }
132 else
133 {
134 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
135 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
136 " in units %s and %s of %s body style." ),
137 nextNumber,
138 nextName,
139 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
140 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
141 prevNumber,
142 pinName,
143 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
144 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
145 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
146 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ),
147 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
148 }
149 }
150 else
151 {
152 if( prev.pin->GetUnit() == 0 || next.pin->GetUnit() == 0 )
153 {
154 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
155 " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
156 nextNumber,
157 nextName,
158 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
159 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
160 prevNumber,
161 pinName,
162 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
163 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ) );
164 }
165 else
166 {
167 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
168 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
169 " in units %s and %s." ),
170 nextNumber,
171 nextName,
172 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
173 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
174 prevNumber,
175 pinName,
176 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
177 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
178 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
179 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ) );
180 }
181 }
182
183 msg += wxT( "<br><br>" );
184 aMessages.push_back( msg );
185 }
186}
187
188
204void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
205 int aGridForPins, UNITS_PROVIDER* aUnitsProvider )
206{
207 if( !aSymbol )
208 return;
209
210 wxString msg;
211
212 // Test reference prefix validity:
213 // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
214 // but it is acceptable if the symbol is saved to a schematic.
215 wxString reference_base = aSymbol->GetReferenceField().GetText();
216
217 if( reference_base.IsEmpty() )
218 {
219 aMessages.push_back( _( "<b>Warning: reference is empty</b><br><br>" ) );
220 }
221 else
222 {
223 wxString illegal_end( wxT( "0123456789?" ) );
224 wxUniChar last_char = reference_base.Last();
225
226 if( illegal_end.Find( last_char ) != wxNOT_FOUND )
227 {
228 msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
229 " issues if saved in a symbol library" ),
230 illegal_end );
231 msg += wxT( "<br><br>" );
232 aMessages.push_back( msg );
233 }
234 }
235
236 CheckDuplicatePins( aSymbol, aMessages, aUnitsProvider );
237
238 std::vector<SCH_PIN*> pinList = aSymbol->GetGraphicalPins( 0, 0 );
239 sort( pinList.begin(), pinList.end(), sort_by_pin_number );
240
241 // The minimal grid size allowed to place a pin is 25 mils
242 // the best grid size is 50 mils, but 25 mils is still usable
243 // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
244 // the wires must be on the 50 mils grid
245 // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
246 const int min_grid_size = schIUScale.MilsToIU( 25 );
247 const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
248
249 // Test for a valid power aSymbol.
250 // A valid power aSymbol has only one unit, no alternate body styles and one pin.
251 // And this pin should be PT_POWER_IN (invisible to be automatically connected)
252 // or PT_POWER_OUT for a power flag
253 if( aSymbol->IsPower() )
254 {
255 if( aSymbol->GetUnitCount() != 1 )
256 {
257 msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
258 aMessages.push_back( msg );
259 }
260
261 if( pinList.size() != 1 )
262 {
263 msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
264 aMessages.push_back( msg );
265 }
266
267 SCH_PIN* pin = pinList[0];
268
269 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
270 && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
271 {
272 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
273 "Only an input or output power pin has meaning<br><br>" ) );
274 aMessages.push_back( msg );
275 }
276
277 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !pin->IsVisible() )
278 {
279 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
280 "Invisible input power pins are no longer required<br><br>" ) );
281 aMessages.push_back( msg );
282 }
283 }
284
285
286 for( SCH_PIN* pin : pinList )
287 {
288 wxString pinName = pin->GetName();
289
290 if( pinName.IsEmpty() || pinName == "~" )
291 pinName = "";
292 else
293 pinName = "'" + pinName + "'";
294
295 if( !aSymbol->IsGlobalPower()
296 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
297 && !pin->IsVisible() )
298 {
299 // hidden power pin
300 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
301 {
302 if( aSymbol->GetUnitCount() <= 1 )
303 {
304 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
305 " in %s body style." ),
306 pin->GetNumber(),
307 pinName,
308 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
309 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
310 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
311 }
312 else
313 {
314 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
315 " in unit %c of %s body style." ),
316 pin->GetNumber(),
317 pinName,
318 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
319 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
320 'A' + pin->GetUnit() - 1,
321 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
322 }
323 }
324 else
325 {
326 if( aSymbol->GetUnitCount() <= 1 )
327 {
328 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>"
329 "(%s, %s)</b>." ),
330 pin->GetNumber(),
331 pinName,
332 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
333 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
334 }
335 else
336 {
337 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
338 " in unit %c." ),
339 pin->GetNumber(),
340 pinName,
341 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
342 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
343 'A' + pin->GetUnit() - 1 );
344 }
345 }
346
347 msg += wxT( "<br>" );
348 msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
349 msg += wxT( "<br><br>" );
350 aMessages.push_back( msg );
351 }
352
353 if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
354 || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
355 {
356 // pin is off grid
357 msg.Empty();
358
359 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
360 {
361 if( aSymbol->GetUnitCount() <= 1 )
362 {
363 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
364 " of %s body style." ),
365 pin->GetNumber(),
366 pinName,
367 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
368 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
369 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
370 }
371 else
372 {
373 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
374 " in unit %c of %s body style." ),
375 pin->GetNumber(),
376 pinName,
377 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
378 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
379 'A' + pin->GetUnit() - 1,
380 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
381 }
382 }
383 else
384 {
385 if( aSymbol->GetUnitCount() <= 1 )
386 {
387 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
388 pin->GetNumber(),
389 pinName,
390 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
391 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
392 }
393 else
394 {
395 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
396 " in unit %c." ),
397 pin->GetNumber(),
398 pinName,
399 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
400 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
401 'A' + pin->GetUnit() - 1 );
402 }
403 }
404
405 msg += wxT( "<br><br>" );
406 aMessages.push_back( msg );
407 }
408 }
409
410 CheckLibSymbolGraphics( aSymbol, aMessages, aUnitsProvider );
411}
412
413
414void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
415 UNITS_PROVIDER* aUnitsProvider )
416{
417 if( !aSymbol )
418 return;
419
420 wxString msg;
421
422 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
423 {
424 if( item.Type() != SCH_SHAPE_T )
425 continue;
426
427 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( &item );
428
429 switch( shape->GetShape() )
430 {
431 case SHAPE_T::ARC:
432 break;
433
434 case SHAPE_T::CIRCLE:
435 if( shape->GetRadius() <= 0 )
436 {
437 msg.Printf( _( "<b>Graphic circle has radius = 0</b> at location "
438 "<b>(%s, %s)</b>." ),
439 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
440 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
441 msg += wxT( "<br>" );
442 aMessages.push_back( msg );
443 }
444 break;
445
447 if( shape->GetPosition() == shape->GetEnd() )
448 {
449 msg.Printf( _( "<b>Graphic rectangle has size 0</b> at location <b>(%s, %s)</b>." ),
450 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
451 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
452 msg += wxT( "<br>" );
453 aMessages.push_back( msg );
454 }
455 break;
456
457 case SHAPE_T::POLY:
458 break;
459
460 case SHAPE_T::BEZIER:
461 break;
462
463 case SHAPE_T::ELLIPSE:
465 if( shape->GetEllipseMajorRadius() <= 0 || shape->GetEllipseMinorRadius() <= 0 )
466 {
467 msg.Printf( _( "<b>Graphic ellipse has null or negative radii</b> at location "
468 "<b>(%s, %s)</b>." ),
469 aUnitsProvider->MessageTextFromValue( shape->GetPosition().x ),
470 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
471 msg += wxT( "<br>" );
472 aMessages.push_back( msg );
473 }
474
475 if( shape->GetShape() == SHAPE_T::ELLIPSE_ARC
476 && shape->GetEllipseStartAngle() == shape->GetEllipseEndAngle() )
477 {
478 msg.Printf( _( "<b>Graphic elliptical arc has zero sweep</b> at location "
479 "<b>(%s, %s)</b>." ),
480 aUnitsProvider->MessageTextFromValue( shape->GetPosition().x ),
481 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
482 msg += wxT( "<br>" );
483 aMessages.push_back( msg );
484 }
485 break;
486
487
488 default:
490 }
491 }
492}
493
494
495bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst )
496{
497 // Use number as primary key
498 int test = ref->GetNumber().Cmp( tst->GetNumber() );
499
500 // Use DeMorgan variant as secondary key
501 if( test == 0 )
502 test = ref->GetBodyStyle() - tst->GetBodyStyle();
503
504 // Use unit as tertiary key
505 if( test == 0 )
506 test = ref->GetUnit() - tst->GetUnit();
507
508 return test < 0;
509}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
int GetEllipseMinorRadius() const
Definition eda_shape.h:310
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:338
int GetEllipseMajorRadius() const
Definition eda_shape.h:301
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:329
wxString SHAPE_T_asString() const
Define a library symbol object.
Definition lib_symbol.h:79
std::vector< const SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:771
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:709
bool IsGlobalPower() const override
wxString GetBodyStyleDescription(int aBodyStyle, bool aLabel) const override
int GetUnitCount() const override
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the user-defined display name for aUnit for symbols with units.
SCH_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition lib_symbol.h:333
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
int GetBodyStyle() const
Definition sch_item.h:242
int GetUnit() const
Definition sch_item.h:233
const wxString & GetName() const
Definition sch_pin.cpp:494
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:345
const wxString & GetNumber() const
Definition sch_pin.h:127
VECTOR2I GetPosition() const override
Definition sch_shape.h:84
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:52
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
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:586
wxString number
expanded logical pin number
Definition lib_symbol.h:588
const SCH_PIN * pin
pointer to the base graphical pin
Definition lib_symbol.h:587
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:146