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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <vector>
25#include <eda_draw_frame.h>
26#include <lib_symbol.h>
27#include <sch_shape.h>
28#include <macros.h>
29
30// helper function to sort pins by pin num
31static bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst );
32
33
34static void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
35 UNITS_PROVIDER* aUnitsProvider );
36
37
38void CheckDuplicatePins( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
39 UNITS_PROVIDER* aUnitsProvider )
40{
41 wxString msg;
42 std::vector<SCH_PIN*> pinList = aSymbol->GetPins();
43
44 struct LOGICAL_PIN
45 {
46 SCH_PIN* pin;
47 wxString number;
48 };
49
50 std::vector<LOGICAL_PIN> logicalPins;
51 logicalPins.reserve( pinList.size() );
52
53 for( SCH_PIN* pin : pinList )
54 {
55 bool valid = false;
56 std::vector<wxString> numbers = pin->GetStackedPinNumbers( &valid );
57
58 if( !valid || numbers.empty() )
59 {
60 logicalPins.push_back( { pin, pin->GetNumber() } );
61 continue;
62 }
63
64 for( const wxString& number : numbers )
65 logicalPins.push_back( { pin, number } );
66 }
67
68 sort( logicalPins.begin(), logicalPins.end(),
69 []( const LOGICAL_PIN& lhs, const LOGICAL_PIN& rhs )
70 {
71 int result = lhs.number.Cmp( rhs.number );
72
73 if( result == 0 )
74 result = lhs.pin->GetBodyStyle() - rhs.pin->GetBodyStyle();
75
76 if( result == 0 )
77 result = lhs.pin->GetUnit() - rhs.pin->GetUnit();
78
79 if( result == 0 && lhs.pin != rhs.pin )
80 return lhs.pin < rhs.pin;
81
82 return result < 0;
83 } );
84
85 for( unsigned ii = 1; ii < logicalPins.size(); ii++ )
86 {
87 LOGICAL_PIN& prev = logicalPins[ii - 1];
88 LOGICAL_PIN& next = logicalPins[ii];
89
90 if( prev.number != next.number )
91 continue;
92
93 if( prev.pin == next.pin )
94 continue;
95
96 // Pins are not duplicated only if they are in different body styles
97 // (but GetBodyStyle() == 0 means common to all body styles)
98 if( prev.pin->GetBodyStyle() != 0 && next.pin->GetBodyStyle() != 0 )
99 {
100 if( prev.pin->GetBodyStyle() != next.pin->GetBodyStyle() )
101 continue;
102 }
103
104 wxString pinName;
105 wxString nextName;
106
107 if( !prev.pin->GetName().IsEmpty() )
108 pinName = " '" + prev.pin->GetName() + "'";
109
110 if( !next.pin->GetName().IsEmpty() )
111 nextName = " '" + next.pin->GetName() + "'";
112
113 auto formatNumberForMessage = []( const SCH_PIN* pin, const wxString& logicalNumber )
114 {
115 wxString shown = pin->GetNumber();
116
117 if( shown == logicalNumber )
118 return logicalNumber;
119
120 return wxString::Format( wxT( "%s (%s)" ), logicalNumber, shown );
121 };
122
123 wxString prevNumber = formatNumberForMessage( prev.pin, prev.number );
124 wxString nextNumber = formatNumberForMessage( next.pin, next.number );
125
126 if( aSymbol->IsMultiBodyStyle() && next.pin->GetBodyStyle() )
127 {
128 if( prev.pin->GetUnit() == 0 || next.pin->GetUnit() == 0 )
129 {
130 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
131 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
132 " in %s body style." ),
133 nextNumber,
134 nextName,
135 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
136 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
137 prevNumber,
138 prev.pin->GetName(),
139 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
140 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
141 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
142 }
143 else
144 {
145 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
146 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
147 " in units %s and %s of %s body style." ),
148 nextNumber,
149 nextName,
150 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
151 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
152 prevNumber,
153 pinName,
154 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
155 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
156 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
157 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ),
158 aSymbol->GetBodyStyleDescription( prev.pin->GetBodyStyle(), true ).Lower() );
159 }
160 }
161 else
162 {
163 if( prev.pin->GetUnit() == 0 || next.pin->GetUnit() == 0 )
164 {
165 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
166 " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
167 nextNumber,
168 nextName,
169 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
170 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
171 prevNumber,
172 pinName,
173 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
174 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ) );
175 }
176 else
177 {
178 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
179 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
180 " in units %s and %s." ),
181 nextNumber,
182 nextName,
183 aUnitsProvider->MessageTextFromValue( next.pin->GetPosition().x ),
184 aUnitsProvider->MessageTextFromValue( -next.pin->GetPosition().y ),
185 prevNumber,
186 pinName,
187 aUnitsProvider->MessageTextFromValue( prev.pin->GetPosition().x ),
188 aUnitsProvider->MessageTextFromValue( -prev.pin->GetPosition().y ),
189 aSymbol->GetUnitDisplayName( next.pin->GetUnit(), false ),
190 aSymbol->GetUnitDisplayName( prev.pin->GetUnit(), false ) );
191 }
192 }
193
194 msg += wxT( "<br><br>" );
195 aMessages.push_back( msg );
196 }
197}
198
199
215void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
216 int aGridForPins, UNITS_PROVIDER* aUnitsProvider )
217{
218 if( !aSymbol )
219 return;
220
221 wxString msg;
222
223 // Test reference prefix validity:
224 // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
225 // but it is acceptable if the symbol is saved to a schematic.
226 wxString reference_base = aSymbol->GetReferenceField().GetText();
227
228 if( reference_base.IsEmpty() )
229 {
230 aMessages.push_back( _( "<b>Warning: reference is empty</b><br><br>" ) );
231 }
232 else
233 {
234 wxString illegal_end( wxT( "0123456789?" ) );
235 wxUniChar last_char = reference_base.Last();
236
237 if( illegal_end.Find( last_char ) != wxNOT_FOUND )
238 {
239 msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
240 " issues if saved in a symbol library" ),
241 illegal_end );
242 msg += wxT( "<br><br>" );
243 aMessages.push_back( msg );
244 }
245 }
246
247 CheckDuplicatePins( aSymbol, aMessages, aUnitsProvider );
248
249 std::vector<SCH_PIN*> pinList = aSymbol->GetPins();
250 sort( pinList.begin(), pinList.end(), sort_by_pin_number );
251
252 // The minimal grid size allowed to place a pin is 25 mils
253 // the best grid size is 50 mils, but 25 mils is still usable
254 // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
255 // the wires must be on the 50 mils grid
256 // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
257 const int min_grid_size = schIUScale.MilsToIU( 25 );
258 const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
259
260 // Test for a valid power aSymbol.
261 // A valid power aSymbol has only one unit, no alternate body styles and one pin.
262 // And this pin should be PT_POWER_IN (invisible to be automatically connected)
263 // or PT_POWER_OUT for a power flag
264 if( aSymbol->IsPower() )
265 {
266 if( aSymbol->GetUnitCount() != 1 )
267 {
268 msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
269 aMessages.push_back( msg );
270 }
271
272 if( pinList.size() != 1 )
273 {
274 msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
275 aMessages.push_back( msg );
276 }
277
278 SCH_PIN* pin = pinList[0];
279
280 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
281 && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
282 {
283 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
284 "Only an input or output power pin has meaning<br><br>" ) );
285 aMessages.push_back( msg );
286 }
287
288 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !pin->IsVisible() )
289 {
290 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
291 "Invisible input power pins are no longer required<br><br>" ) );
292 aMessages.push_back( msg );
293 }
294 }
295
296
297 for( SCH_PIN* pin : pinList )
298 {
299 wxString pinName = pin->GetName();
300
301 if( pinName.IsEmpty() || pinName == "~" )
302 pinName = "";
303 else
304 pinName = "'" + pinName + "'";
305
306 if( !aSymbol->IsGlobalPower()
307 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
308 && !pin->IsVisible() )
309 {
310 // hidden power pin
311 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
312 {
313 if( aSymbol->GetUnitCount() <= 1 )
314 {
315 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
316 " in %s body style." ),
317 pin->GetNumber(),
318 pinName,
319 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
320 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
321 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
322 }
323 else
324 {
325 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
326 " in unit %c of %s body style." ),
327 pin->GetNumber(),
328 pinName,
329 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
330 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
331 'A' + pin->GetUnit() - 1,
332 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
333 }
334 }
335 else
336 {
337 if( aSymbol->GetUnitCount() <= 1 )
338 {
339 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>"
340 "(%s, %s)</b>." ),
341 pin->GetNumber(),
342 pinName,
343 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
344 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
345 }
346 else
347 {
348 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
349 " in unit %c." ),
350 pin->GetNumber(),
351 pinName,
352 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
353 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
354 'A' + pin->GetUnit() - 1 );
355 }
356 }
357
358 msg += wxT( "<br>" );
359 msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
360 msg += wxT( "<br><br>" );
361 aMessages.push_back( msg );
362 }
363
364 if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
365 || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
366 {
367 // pin is off grid
368 msg.Empty();
369
370 if( aSymbol->IsMultiBodyStyle() && pin->GetBodyStyle() )
371 {
372 if( aSymbol->GetUnitCount() <= 1 )
373 {
374 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
375 " of %s body style." ),
376 pin->GetNumber(),
377 pinName,
378 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
379 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
380 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
381 }
382 else
383 {
384 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
385 " in unit %c of %s body style." ),
386 pin->GetNumber(),
387 pinName,
388 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
389 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
390 'A' + pin->GetUnit() - 1,
391 aSymbol->GetBodyStyleDescription( pin->GetBodyStyle(), true ).Lower() );
392 }
393 }
394 else
395 {
396 if( aSymbol->GetUnitCount() <= 1 )
397 {
398 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
399 pin->GetNumber(),
400 pinName,
401 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
402 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
403 }
404 else
405 {
406 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
407 " in unit %c." ),
408 pin->GetNumber(),
409 pinName,
410 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
411 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
412 'A' + pin->GetUnit() - 1 );
413 }
414 }
415
416 msg += wxT( "<br><br>" );
417 aMessages.push_back( msg );
418 }
419 }
420
421 CheckLibSymbolGraphics( aSymbol, aMessages, aUnitsProvider );
422}
423
424
425void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
426 UNITS_PROVIDER* aUnitsProvider )
427{
428 if( !aSymbol )
429 return;
430
431 wxString msg;
432
433 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
434 {
435 if( item.Type() != SCH_SHAPE_T )
436 continue;
437
438 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( &item );
439
440 switch( shape->GetShape() )
441 {
442 case SHAPE_T::ARC:
443 break;
444
445 case SHAPE_T::CIRCLE:
446 if( shape->GetRadius() <= 0 )
447 {
448 msg.Printf( _( "<b>Graphic circle has radius = 0</b> at location "
449 "<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
458 if( shape->GetPosition() == shape->GetEnd() )
459 {
460 msg.Printf( _( "<b>Graphic rectangle has size 0</b> at location <b>(%s, %s)</b>." ),
461 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
462 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
463 msg += wxT( "<br>" );
464 aMessages.push_back( msg );
465 }
466 break;
467
468 case SHAPE_T::POLY:
469 break;
470
471 case SHAPE_T::BEZIER:
472 break;
473
474 default:
476 }
477 }
478}
479
480
481bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst )
482{
483 // Use number as primary key
484 int test = ref->GetNumber().Cmp( tst->GetNumber() );
485
486 // Use DeMorgan variant as secondary key
487 if( test == 0 )
488 test = ref->GetBodyStyle() - tst->GetBodyStyle();
489
490 // Use unit as tertiary key
491 if( test == 0 )
492 test = ref->GetUnit() - tst->GetUnit();
493
494 return test < 0;
495}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
wxString SHAPE_T_asString() const
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
Define a library symbol object.
Definition lib_symbol.h:87
bool IsPower() const override
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:752
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:690
std::vector< SCH_PIN * > GetPins() const override
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:342
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
int GetBodyStyle() const
Definition sch_item.h:247
int GetUnit() const
Definition sch_item.h:238
const wxString & GetNumber() const
Definition sch_pin.h:124
VECTOR2I GetPosition() const override
Definition sch_shape.h:85
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
#define _(s)
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:47
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
CITER next(CITER it)
Definition ptree.cpp:124
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)
@ SCH_SHAPE_T
Definition typeinfo.h:153