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