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