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 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<SCH_PIN*> pinList = aSymbol->GetPins();
74
75 // Test for duplicates:
76 // Sort pins by pin num, so 2 duplicate pins
77 // (pins with the same number) will be consecutive in list
78 sort( pinList.begin(), pinList.end(), sort_by_pin_number );
79
80 // The minimal grid size allowed to place a pin is 25 mils
81 // the best grid size is 50 mils, but 25 mils is still usable
82 // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
83 // the wires must be on the 50 mils grid
84 // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
85 const int min_grid_size = schIUScale.MilsToIU( 25 );
86 const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
87
88 for( unsigned ii = 1; ii < pinList.size(); ii++ )
89 {
90 SCH_PIN* pin = pinList[ii - 1];
91 SCH_PIN* next = pinList[ii];
92
93 if( pin->GetNumber() != next->GetNumber() )
94 continue;
95
96 // Pins are not duplicated only if they are in different body styles
97 // (but GetBodyStyle() == 0 means commun to all body styles)
98 if( pin->GetBodyStyle() != 0 && next->GetBodyStyle() != 0 )
99 {
100 if( pin->GetBodyStyle() != next->GetBodyStyle() )
101 continue;
102 }
103
104 wxString pinName;
105 wxString nextName;
106
107 if( pin->GetName() != "~" && !pin->GetName().IsEmpty() )
108 pinName = " '" + pin->GetName() + "'";
109
110 if( next->GetName() != "~" && !next->GetName().IsEmpty() )
111 nextName = " '" + next->GetName() + "'";
112
113 if( aSymbol->HasAlternateBodyStyle() && next->GetBodyStyle() )
114 {
115 if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
116 {
117 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
118 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
119 " in %s body style." ),
120 next->GetNumber(),
121 nextName,
122 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
123 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
124 pin->GetNumber(),
125 pin->GetName(),
126 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
127 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
128 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
129 }
130 else
131 {
132 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
133 " conflicts with pin %s%s at location <b>(%s, %s)</b>"
134 " in units %s and %s of %s body style." ),
135 next->GetNumber(),
136 nextName,
137 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
138 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
139 pin->GetNumber(),
140 pinName,
141 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
142 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
143 aSymbol->GetUnitReference( next->GetUnit() ),
144 aSymbol->GetUnitReference( pin->GetUnit() ),
145 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
146 }
147 }
148 else
149 {
150 if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
151 {
152 msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
153 " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
154 next->GetNumber(),
155 nextName,
156 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
157 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
158 pin->GetNumber(),
159 pinName,
160 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
161 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
162 }
163 else
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 " in units %s and %s." ),
168 next->GetNumber(),
169 nextName,
170 aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
171 aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
172 pin->GetNumber(),
173 pinName,
174 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
175 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
176 aSymbol->GetUnitReference( next->GetUnit() ),
177 aSymbol->GetUnitReference( pin->GetUnit() ) );
178 }
179 }
180
181 msg += wxT( "<br><br>" );
182 aMessages.push_back( msg );
183 }
184
185 // Test for a valid power aSymbol.
186 // A valid power aSymbol has only one unit, no alternate body styles and one pin.
187 // And this pin should be PT_POWER_IN (invisible to be automatically connected)
188 // or PT_POWER_OUT for a power flag
189 if( aSymbol->IsPower() )
190 {
191 if( aSymbol->GetUnitCount() != 1 )
192 {
193 msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
194 aMessages.push_back( msg );
195 }
196
197 if( aSymbol->HasAlternateBodyStyle() )
198 {
199 msg.Printf( _( "<b>A Power Symbol should not have DeMorgan variants</b><br><br>" ) );
200 aMessages.push_back( msg );
201 }
202
203 if( pinList.size() != 1 )
204 {
205 msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
206 aMessages.push_back( msg );
207 }
208
209 SCH_PIN* pin = pinList[0];
210
211 if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
212 && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
213 {
214 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
215 "Only an input or output power pin has meaning<br><br>" ) );
216 aMessages.push_back( msg );
217 }
218
219 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !pin->IsVisible() )
220 {
221 msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
222 "Invisible input power pins are no longer required<br><br>" ) );
223 aMessages.push_back( msg );
224 }
225 }
226
227
228 for( SCH_PIN* pin : pinList )
229 {
230 wxString pinName = pin->GetName();
231
232 if( pinName.IsEmpty() || pinName == "~" )
233 pinName = "";
234 else
235 pinName = "'" + pinName + "'";
236
237 if( !aSymbol->IsPower()
238 && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
239 && !pin->IsVisible() )
240 {
241 // hidden power pin
242 if( aSymbol->HasAlternateBodyStyle() && pin->GetBodyStyle() )
243 {
244 if( aSymbol->GetUnitCount() <= 1 )
245 {
246 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
247 " in %s body style." ),
248 pin->GetNumber(),
249 pinName,
250 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
251 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
252 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
253 }
254 else
255 {
256 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
257 " in unit %c of %s body style." ),
258 pin->GetNumber(),
259 pinName,
260 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
261 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
262 'A' + pin->GetUnit() - 1,
263 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
264 }
265 }
266 else
267 {
268 if( aSymbol->GetUnitCount() <= 1 )
269 {
270 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>." ),
271 pin->GetNumber(),
272 pinName,
273 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
274 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
275 }
276 else
277 {
278 msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
279 " in unit %c." ),
280 pin->GetNumber(),
281 pinName,
282 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
283 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
284 'A' + pin->GetUnit() - 1 );
285 }
286 }
287
288 msg += wxT( "<br>" );
289 msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
290 msg += wxT( "<br><br>" );
291 aMessages.push_back( msg );
292 }
293
294 if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
295 || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
296 {
297 // pin is off grid
298 msg.Empty();
299
300 if( aSymbol->HasAlternateBodyStyle() && pin->GetBodyStyle() )
301 {
302 if( aSymbol->GetUnitCount() <= 1 )
303 {
304 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
305 " of %s body style." ),
306 pin->GetNumber(),
307 pinName,
308 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
309 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
310 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
311 }
312 else
313 {
314 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
315 " in unit %c of %s body style." ),
316 pin->GetNumber(),
317 pinName,
318 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
319 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
320 'A' + pin->GetUnit() - 1,
321 SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
322 }
323 }
324 else
325 {
326 if( aSymbol->GetUnitCount() <= 1 )
327 {
328 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
329 pin->GetNumber(),
330 pinName,
331 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
332 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
333 }
334 else
335 {
336 msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
337 " in unit %c." ),
338 pin->GetNumber(),
339 pinName,
340 aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
341 aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
342 'A' + pin->GetUnit() - 1 );
343 }
344 }
345
346 msg += wxT( "<br><br>" );
347 aMessages.push_back( msg );
348 }
349 }
350
351 CheckLibSymbolGraphics( aSymbol, aMessages, aUnitsProvider );
352}
353
354
355void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
356 EDA_DRAW_FRAME* aUnitsProvider )
357{
358 if( !aSymbol )
359 return;
360
361 wxString msg;
362
363 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
364 {
365 if( item.Type() != SCH_SHAPE_T )
366 continue;
367
368 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( &item );
369
370 switch( shape->GetShape() )
371 {
372 case SHAPE_T::ARC:
373 break;
374
375 case SHAPE_T::CIRCLE:
376 if( shape->GetRadius() <= 0 )
377 {
378 msg.Printf( _( "<b>Graphic circle has radius = 0</b> at location <b>(%s, %s)</b>." ),
379 aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
380 aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
381 msg += wxT( "<br>" );
382 aMessages.push_back( msg );
383 }
384 break;
385
387 if( shape->GetPosition() == shape->GetEnd() )
388 {
389 msg.Printf( _( "<b>Graphic rectangle has size 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
397 case SHAPE_T::POLY:
398 break;
399
400 case SHAPE_T::BEZIER:
401 break;
402
403 default:
405 }
406 }
407}
408
409
410bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst )
411{
412 // Use number as primary key
413 int test = ref->GetNumber().Cmp( tst->GetNumber() );
414
415 // Use DeMorgan variant as secondary key
416 if( test == 0 )
417 test = ref->GetBodyStyle() - tst->GetBodyStyle();
418
419 // Use unit as tertiary key
420 if( test == 0 )
421 test = ref->GetUnit() - tst->GetUnit();
422
423 return test < 0;
424}
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:593
SHAPE_T GetShape() const
Definition: eda_shape.h:120
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:150
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:98
Define a library symbol object.
Definition: lib_symbol.h:77
bool IsPower() const override
Definition: lib_symbol.cpp:663
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:982
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:486
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:523
int GetUnitCount() const override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:174
int GetBodyStyle() const
Definition: sch_item.h:240
int GetUnit() const
Definition: sch_item.h:237
static wxString GetBodyStyleDescription(int aBodyStyle)
Definition: sch_item.cpp:60
const wxString & GetNumber() const
Definition: sch_pin.h:111
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)
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 ...
@ SCH_SHAPE_T
Definition: typeinfo.h:149