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