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