You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

441 lines
18 KiB

2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
2 years ago
  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. #include <vector>
  24. #include <eda_draw_frame.h>
  25. #include <lib_symbol.h>
  26. #include <sch_shape.h>
  27. #include <macros.h>
  28. // helper function to sort pins by pin num
  29. static bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst );
  30. static void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  31. UNITS_PROVIDER* aUnitsProvider );
  32. void CheckDuplicatePins( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  33. UNITS_PROVIDER* aUnitsProvider )
  34. {
  35. wxString msg;
  36. std::vector<SCH_PIN*> pinList = aSymbol->GetPins();
  37. // Test for duplicates:
  38. // Sort pins by pin num, so 2 duplicate pins
  39. // (pins with the same number) will be consecutive in list
  40. sort( pinList.begin(), pinList.end(), sort_by_pin_number );
  41. for( unsigned ii = 1; ii < pinList.size(); ii++ )
  42. {
  43. SCH_PIN* pin = pinList[ii - 1];
  44. SCH_PIN* next = pinList[ii];
  45. if( pin->GetNumber() != next->GetNumber() )
  46. continue;
  47. // Pins are not duplicated only if they are in different body styles
  48. // (but GetBodyStyle() == 0 means common to all body styles)
  49. if( pin->GetBodyStyle() != 0 && next->GetBodyStyle() != 0 )
  50. {
  51. if( pin->GetBodyStyle() != next->GetBodyStyle() )
  52. continue;
  53. }
  54. wxString pinName;
  55. wxString nextName;
  56. if( !pin->GetName().IsEmpty() )
  57. pinName = " '" + pin->GetName() + "'";
  58. if( !next->GetName().IsEmpty() )
  59. nextName = " '" + next->GetName() + "'";
  60. if( aSymbol->HasAlternateBodyStyle() && next->GetBodyStyle() )
  61. {
  62. if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
  63. {
  64. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  65. " conflicts with pin %s%s at location <b>(%s, %s)</b>"
  66. " in %s body style." ),
  67. next->GetNumber(),
  68. nextName,
  69. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  70. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  71. pin->GetNumber(),
  72. pin->GetName(),
  73. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  74. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  75. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  76. }
  77. else
  78. {
  79. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  80. " conflicts with pin %s%s at location <b>(%s, %s)</b>"
  81. " in units %s and %s of %s body style." ),
  82. next->GetNumber(),
  83. nextName,
  84. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  85. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  86. pin->GetNumber(),
  87. pinName,
  88. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  89. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  90. aSymbol->GetUnitReference( next->GetUnit() ),
  91. aSymbol->GetUnitReference( pin->GetUnit() ),
  92. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  93. }
  94. }
  95. else
  96. {
  97. if( pin->GetUnit() == 0 || next->GetUnit() == 0 )
  98. {
  99. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  100. " conflicts with pin %s%s at location <b>(%s, %s)</b>." ),
  101. next->GetNumber(),
  102. nextName,
  103. aUnitsProvider->MessageTextFromValue( next->GetPosition().x ),
  104. aUnitsProvider->MessageTextFromValue( -next->GetPosition().y ),
  105. pin->GetNumber(),
  106. pinName,
  107. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  108. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  109. }
  110. else
  111. {
  112. msg.Printf( _( "<b>Duplicate pin %s</b> %s at location <b>(%s, %s)</b>"
  113. " conflicts with pin %s%s at location <b>(%s, %s)</b>"
  114. " in units %s and %s." ),
  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. aSymbol->GetUnitReference( next->GetUnit() ),
  124. aSymbol->GetUnitReference( pin->GetUnit() ) );
  125. }
  126. }
  127. msg += wxT( "<br><br>" );
  128. aMessages.push_back( msg );
  129. }
  130. }
  131. /**
  132. * Check a library symbol to find incorrect settings.
  133. *
  134. * - Pins not on a valid grid
  135. * - Pins duplicated
  136. * - Conflict with pins at same location
  137. * - Incorrect Power Symbols
  138. * - illegal reference prefix (cannot ends by a digit or a '?')
  139. *
  140. * @param aSymbol is the library symbol to check.
  141. * @param aMessages is a room to store error messages.
  142. * @param aGridForPins (in IU) is the grid to test pin positions ( >= 25 mils )
  143. * should be 25, 50 or 100 mils (converted to IUs).
  144. * @param aUnitsProvider a frame to format coordinates in messages.
  145. */
  146. void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  147. int aGridForPins, UNITS_PROVIDER* aUnitsProvider )
  148. {
  149. if( !aSymbol )
  150. return;
  151. wxString msg;
  152. // Test reference prefix validity:
  153. // if the symbol is saved in a library, the prefix should not ends by a digit or a '?'
  154. // but it is acceptable if the symbol is saved to a schematic.
  155. wxString reference_base = aSymbol->GetReferenceField().GetText();
  156. wxString illegal_end( wxT( "0123456789?" ) );
  157. wxUniChar last_char = reference_base.Last();
  158. if( illegal_end.Find( last_char ) != wxNOT_FOUND )
  159. {
  160. msg.Printf( _( "<b>Warning: reference prefix</b><br>prefix ending by '%s' can create"
  161. " issues if saved in a symbol library" ),
  162. illegal_end );
  163. msg += wxT( "<br><br>" );
  164. aMessages.push_back( msg );
  165. }
  166. CheckDuplicatePins( aSymbol, aMessages, aUnitsProvider );
  167. std::vector<SCH_PIN*> pinList = aSymbol->GetPins();
  168. sort( pinList.begin(), pinList.end(), sort_by_pin_number );
  169. // The minimal grid size allowed to place a pin is 25 mils
  170. // the best grid size is 50 mils, but 25 mils is still usable
  171. // this is because all aSymbols are using a 50 mils grid to place pins, and therefore
  172. // the wires must be on the 50 mils grid
  173. // So raise an error if a pin is not on a 25 (or bigger :50 or 100) mils grid
  174. const int min_grid_size = schIUScale.MilsToIU( 25 );
  175. const int clamped_grid_size = ( aGridForPins < min_grid_size ) ? min_grid_size : aGridForPins;
  176. // Test for a valid power aSymbol.
  177. // A valid power aSymbol has only one unit, no alternate body styles and one pin.
  178. // And this pin should be PT_POWER_IN (invisible to be automatically connected)
  179. // or PT_POWER_OUT for a power flag
  180. if( aSymbol->IsPower() )
  181. {
  182. if( aSymbol->GetUnitCount() != 1 )
  183. {
  184. msg.Printf( _( "<b>A Power Symbol should have only one unit</b><br><br>" ) );
  185. aMessages.push_back( msg );
  186. }
  187. if( aSymbol->HasAlternateBodyStyle() )
  188. {
  189. msg.Printf( _( "<b>A Power Symbol should not have DeMorgan variants</b><br><br>" ) );
  190. aMessages.push_back( msg );
  191. }
  192. if( pinList.size() != 1 )
  193. {
  194. msg.Printf( _( "<b>A Power Symbol should have only one pin</b><br><br>" ) );
  195. aMessages.push_back( msg );
  196. }
  197. SCH_PIN* pin = pinList[0];
  198. if( pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_IN
  199. && pin->GetType() != ELECTRICAL_PINTYPE::PT_POWER_OUT )
  200. {
  201. msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
  202. "Only an input or output power pin has meaning<br><br>" ) );
  203. aMessages.push_back( msg );
  204. }
  205. if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !pin->IsVisible() )
  206. {
  207. msg.Printf( _( "<b>Suspicious Power Symbol</b><br>"
  208. "Invisible input power pins are no longer required<br><br>" ) );
  209. aMessages.push_back( msg );
  210. }
  211. }
  212. for( SCH_PIN* pin : pinList )
  213. {
  214. wxString pinName = pin->GetName();
  215. if( pinName.IsEmpty() || pinName == "~" )
  216. pinName = "";
  217. else
  218. pinName = "'" + pinName + "'";
  219. if( !aSymbol->IsGlobalPower()
  220. && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN
  221. && !pin->IsVisible() )
  222. {
  223. // hidden power pin
  224. if( aSymbol->HasAlternateBodyStyle() && pin->GetBodyStyle() )
  225. {
  226. if( aSymbol->GetUnitCount() <= 1 )
  227. {
  228. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  229. " in %s body style." ),
  230. pin->GetNumber(),
  231. pinName,
  232. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  233. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  234. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  235. }
  236. else
  237. {
  238. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  239. " in unit %c of %s body style." ),
  240. pin->GetNumber(),
  241. pinName,
  242. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  243. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  244. 'A' + pin->GetUnit() - 1,
  245. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  246. }
  247. }
  248. else
  249. {
  250. if( aSymbol->GetUnitCount() <= 1 )
  251. {
  252. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>"
  253. "(%s, %s)</b>." ),
  254. pin->GetNumber(),
  255. pinName,
  256. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  257. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  258. }
  259. else
  260. {
  261. msg.Printf( _( "Info: <b>Hidden power pin %s</b> %s at location <b>(%s, %s)</b>"
  262. " in unit %c." ),
  263. pin->GetNumber(),
  264. pinName,
  265. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  266. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  267. 'A' + pin->GetUnit() - 1 );
  268. }
  269. }
  270. msg += wxT( "<br>" );
  271. msg += _( "(Hidden power pins will drive their pin names on to any connected nets.)" );
  272. msg += wxT( "<br><br>" );
  273. aMessages.push_back( msg );
  274. }
  275. if( ( (pin->GetPosition().x % clamped_grid_size) != 0 )
  276. || ( (pin->GetPosition().y % clamped_grid_size) != 0 ) )
  277. {
  278. // pin is off grid
  279. msg.Empty();
  280. if( aSymbol->HasAlternateBodyStyle() && pin->GetBodyStyle() )
  281. {
  282. if( aSymbol->GetUnitCount() <= 1 )
  283. {
  284. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
  285. " of %s body style." ),
  286. pin->GetNumber(),
  287. pinName,
  288. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  289. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  290. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  291. }
  292. else
  293. {
  294. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
  295. " in unit %c of %s body style." ),
  296. pin->GetNumber(),
  297. pinName,
  298. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  299. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  300. 'A' + pin->GetUnit() - 1,
  301. SCH_ITEM::GetBodyStyleDescription( pin->GetBodyStyle() ).Lower() );
  302. }
  303. }
  304. else
  305. {
  306. if( aSymbol->GetUnitCount() <= 1 )
  307. {
  308. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>." ),
  309. pin->GetNumber(),
  310. pinName,
  311. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  312. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ) );
  313. }
  314. else
  315. {
  316. msg.Printf( _( "<b>Off grid pin %s</b> %s at location <b>(%s, %s)</b>"
  317. " in unit %c." ),
  318. pin->GetNumber(),
  319. pinName,
  320. aUnitsProvider->MessageTextFromValue( pin->GetPosition().x ),
  321. aUnitsProvider->MessageTextFromValue( -pin->GetPosition().y ),
  322. 'A' + pin->GetUnit() - 1 );
  323. }
  324. }
  325. msg += wxT( "<br><br>" );
  326. aMessages.push_back( msg );
  327. }
  328. }
  329. CheckLibSymbolGraphics( aSymbol, aMessages, aUnitsProvider );
  330. }
  331. void CheckLibSymbolGraphics( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
  332. UNITS_PROVIDER* aUnitsProvider )
  333. {
  334. if( !aSymbol )
  335. return;
  336. wxString msg;
  337. for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
  338. {
  339. if( item.Type() != SCH_SHAPE_T )
  340. continue;
  341. const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( &item );
  342. switch( shape->GetShape() )
  343. {
  344. case SHAPE_T::ARC:
  345. break;
  346. case SHAPE_T::CIRCLE:
  347. if( shape->GetRadius() <= 0 )
  348. {
  349. msg.Printf( _( "<b>Graphic circle has radius = 0</b> at location "
  350. "<b>(%s, %s)</b>." ),
  351. aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
  352. aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
  353. msg += wxT( "<br>" );
  354. aMessages.push_back( msg );
  355. }
  356. break;
  357. case SHAPE_T::RECTANGLE:
  358. if( shape->GetPosition() == shape->GetEnd() )
  359. {
  360. msg.Printf( _( "<b>Graphic rectangle has size 0</b> at location <b>(%s, %s)</b>." ),
  361. aUnitsProvider->MessageTextFromValue(shape->GetPosition().x ),
  362. aUnitsProvider->MessageTextFromValue( -shape->GetPosition().y ) );
  363. msg += wxT( "<br>" );
  364. aMessages.push_back( msg );
  365. }
  366. break;
  367. case SHAPE_T::POLY:
  368. break;
  369. case SHAPE_T::BEZIER:
  370. break;
  371. default:
  372. UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
  373. }
  374. }
  375. }
  376. bool sort_by_pin_number( const SCH_PIN* ref, const SCH_PIN* tst )
  377. {
  378. // Use number as primary key
  379. int test = ref->GetNumber().Cmp( tst->GetNumber() );
  380. // Use DeMorgan variant as secondary key
  381. if( test == 0 )
  382. test = ref->GetBodyStyle() - tst->GetBodyStyle();
  383. // Use unit as tertiary key
  384. if( test == 0 )
  385. test = ref->GetUnit() - tst->GetUnit();
  386. return test < 0;
  387. }