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.

559 lines
14 KiB

5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
  5. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file pcbnew_scripting_helpers.cpp
  26. * @brief Scripting helper functions for pcbnew functionality
  27. */
  28. #include <Python.h>
  29. #undef HAVE_CLOCK_GETTIME // macro is defined in Python.h and causes redefine warning
  30. #include "pcbnew_scripting_helpers.h"
  31. #include <tool/tool_manager.h>
  32. #include <action_plugin.h>
  33. #include <board.h>
  34. #include <board_design_settings.h>
  35. #include <pcb_marker.h>
  36. #include <cstdlib>
  37. #include <drc/drc_engine.h>
  38. #include <drc/drc_item.h>
  39. #include <fp_lib_table.h>
  40. #include <ignore.h>
  41. #include <io_mgr.h>
  42. #include <string_utils.h>
  43. #include <macros.h>
  44. #include <pcbnew_scripting_helpers.h>
  45. #include <project.h>
  46. #include <project/net_settings.h>
  47. #include <project/project_file.h>
  48. #include <settings/settings_manager.h>
  49. #include <specctra.h>
  50. #include <project/project_local_settings.h>
  51. #include <wildcards_and_files_ext.h>
  52. #include <locale_io.h>
  53. #include <wx/app.h>
  54. static PCB_EDIT_FRAME* s_PcbEditFrame = nullptr;
  55. static SETTINGS_MANAGER* s_SettingsManager = nullptr;
  56. BOARD* GetBoard()
  57. {
  58. if( s_PcbEditFrame )
  59. return s_PcbEditFrame->GetBoard();
  60. else
  61. return nullptr;
  62. }
  63. void ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPcbEditFrame )
  64. {
  65. s_PcbEditFrame = aPcbEditFrame;
  66. }
  67. BOARD* LoadBoard( wxString& aFileName )
  68. {
  69. if( aFileName.EndsWith( KiCadPcbFileExtension ) )
  70. return LoadBoard( aFileName, IO_MGR::KICAD_SEXP );
  71. else if( aFileName.EndsWith( LegacyPcbFileExtension ) )
  72. return LoadBoard( aFileName, IO_MGR::LEGACY );
  73. // as fall back for any other kind use the legacy format
  74. return LoadBoard( aFileName, IO_MGR::LEGACY );
  75. }
  76. SETTINGS_MANAGER* GetSettingsManager()
  77. {
  78. if( !s_SettingsManager )
  79. {
  80. if( s_PcbEditFrame )
  81. {
  82. s_SettingsManager = s_PcbEditFrame->GetSettingsManager();
  83. }
  84. else
  85. {
  86. // Ensure wx system settings stuff is available
  87. ignore_unused( wxTheApp );
  88. s_SettingsManager = new SETTINGS_MANAGER( true );
  89. }
  90. }
  91. return s_SettingsManager;
  92. }
  93. PROJECT* GetDefaultProject()
  94. {
  95. // For some reasons, LoadProject() needs a C locale, so ensure we have the right locale
  96. // This is mainly when running QA Python tests
  97. LOCALE_IO dummy;
  98. PROJECT* project = GetSettingsManager()->GetProject( "" );
  99. if( !project )
  100. {
  101. GetSettingsManager()->LoadProject( "" );
  102. project = GetSettingsManager()->GetProject( "" );
  103. }
  104. return project;
  105. }
  106. BOARD* LoadBoard( wxString& aFileName, IO_MGR::PCB_FILE_T aFormat )
  107. {
  108. wxFileName pro = aFileName;
  109. pro.SetExt( ProjectFileExtension );
  110. pro.MakeAbsolute();
  111. wxString projectPath = pro.GetFullPath();
  112. // Ensure the "C" locale is temporary set, before reading any file
  113. // It also avoid wxWidget alerts about locale issues, later, when using Python 3
  114. LOCALE_IO dummy;
  115. PROJECT* project = GetSettingsManager()->GetProject( projectPath );
  116. if( !project )
  117. {
  118. if( wxFileExists( projectPath ) )
  119. {
  120. GetSettingsManager()->LoadProject( projectPath, false );
  121. project = GetSettingsManager()->GetProject( projectPath );
  122. }
  123. }
  124. else if( s_PcbEditFrame && project == &GetSettingsManager()->Prj() )
  125. {
  126. // Project is already loaded? Then so is the board
  127. return s_PcbEditFrame->GetBoard();
  128. }
  129. // Board cannot be loaded without a project, so create the default project
  130. if( !project )
  131. project = GetDefaultProject();
  132. BOARD* brd = IO_MGR::Load( aFormat, aFileName );
  133. if( brd )
  134. {
  135. brd->SetProject( project );
  136. if( brd->m_LegacyDesignSettingsLoaded )
  137. project->GetProjectFile().NetSettings().RebuildNetClassAssignments();
  138. // Move legacy view settings to local project settings
  139. if( !brd->m_LegacyVisibleLayers.test( Rescue ) )
  140. project->GetLocalSettings().m_VisibleLayers = brd->m_LegacyVisibleLayers;
  141. if( !brd->m_LegacyVisibleItems.test( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) ) )
  142. project->GetLocalSettings().m_VisibleItems = brd->m_LegacyVisibleItems;
  143. BOARD_DESIGN_SETTINGS& bds = brd->GetDesignSettings();
  144. bds.m_DRCEngine = std::make_shared<DRC_ENGINE>( brd, &bds );
  145. try
  146. {
  147. wxFileName rules = pro;
  148. rules.SetExt( DesignRulesFileExtension );
  149. bds.m_DRCEngine->InitEngine( rules );
  150. }
  151. catch( ... )
  152. {
  153. // Best efforts...
  154. }
  155. for( PCB_MARKER* marker : brd->ResolveDRCExclusions() )
  156. brd->Add( marker );
  157. brd->BuildConnectivity();
  158. brd->BuildListOfNets();
  159. brd->SynchronizeNetsAndNetClasses();
  160. }
  161. return brd;
  162. }
  163. BOARD* NewBoard( wxString& aFileName )
  164. {
  165. wxFileName boardFn = aFileName;
  166. wxFileName proFn = aFileName;
  167. proFn.SetExt( ProjectFileExtension );
  168. proFn.MakeAbsolute();
  169. wxString projectPath = proFn.GetFullPath();
  170. // Ensure the "C" locale is temporary set, before reading any file
  171. // It also avoids wxWidgets alerts about locale issues, later, when using Python 3
  172. LOCALE_IO dummy;
  173. GetSettingsManager()->LoadProject( projectPath, false );
  174. PROJECT* project = GetSettingsManager()->GetProject( projectPath );
  175. BOARD* brd = new BOARD();
  176. brd->SetProject( project );
  177. BOARD_DESIGN_SETTINGS& bds = brd->GetDesignSettings();
  178. bds.m_DRCEngine = std::make_shared<DRC_ENGINE>( brd, &bds );
  179. SaveBoard( aFileName, brd );
  180. return brd;
  181. }
  182. BOARD* CreateEmptyBoard()
  183. {
  184. // Creating a new board is not possible if running inside KiCad
  185. if( s_PcbEditFrame )
  186. return nullptr;
  187. BOARD* brd = new BOARD();
  188. brd->SetProject( GetDefaultProject() );
  189. return brd;
  190. }
  191. bool SaveBoard( wxString& aFileName, BOARD* aBoard, IO_MGR::PCB_FILE_T aFormat, bool aSkipSettings )
  192. {
  193. aBoard->BuildConnectivity();
  194. aBoard->SynchronizeNetsAndNetClasses();
  195. try
  196. {
  197. IO_MGR::Save( aFormat, aFileName, aBoard, nullptr );
  198. }
  199. catch( ... )
  200. {
  201. return false;
  202. }
  203. if( !aSkipSettings )
  204. {
  205. wxFileName pro = aFileName;
  206. pro.SetExt( ProjectFileExtension );
  207. pro.MakeAbsolute();
  208. wxString projectPath = pro.GetFullPath();
  209. GetSettingsManager()->SaveProjectAs( pro.GetFullPath(), aBoard->GetProject() );
  210. }
  211. return true;
  212. }
  213. bool SaveBoard( wxString& aFileName, BOARD* aBoard, bool aSkipSettings )
  214. {
  215. return SaveBoard( aFileName, aBoard, IO_MGR::KICAD_SEXP, aSkipSettings );
  216. }
  217. FP_LIB_TABLE* GetFootprintLibraryTable()
  218. {
  219. BOARD* board = GetBoard();
  220. if( !board )
  221. return nullptr;
  222. PROJECT* project = board->GetProject();
  223. if( !project )
  224. return nullptr;
  225. return project->PcbFootprintLibs();
  226. }
  227. wxArrayString GetFootprintLibraries()
  228. {
  229. wxArrayString footprintLibraryNames;
  230. FP_LIB_TABLE* tbl = GetFootprintLibraryTable();
  231. if( !tbl )
  232. return footprintLibraryNames;
  233. for( const wxString& name : tbl->GetLogicalLibs() )
  234. footprintLibraryNames.Add( name );
  235. return footprintLibraryNames;
  236. }
  237. wxArrayString GetFootprints( const wxString& aNickName )
  238. {
  239. wxArrayString footprintNames;
  240. FP_LIB_TABLE* tbl = GetFootprintLibraryTable();
  241. if( !tbl )
  242. return footprintNames;
  243. tbl->FootprintEnumerate( footprintNames, aNickName, true );
  244. return footprintNames;
  245. }
  246. bool ExportSpecctraDSN( wxString& aFullFilename )
  247. {
  248. if( s_PcbEditFrame )
  249. {
  250. bool ok = s_PcbEditFrame->ExportSpecctraFile( aFullFilename );
  251. return ok;
  252. }
  253. else
  254. {
  255. return false;
  256. }
  257. }
  258. bool ExportSpecctraDSN( BOARD* aBoard, wxString& aFullFilename )
  259. {
  260. try
  261. {
  262. ExportBoardToSpecctraFile( aBoard, aFullFilename );
  263. }
  264. catch( ... )
  265. {
  266. return false;
  267. }
  268. return true;
  269. }
  270. bool ExportVRML( const wxString& aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles,
  271. bool aUseRelativePaths, const wxString& a3D_Subdir, double aXRef, double aYRef )
  272. {
  273. if( s_PcbEditFrame )
  274. {
  275. bool ok = s_PcbEditFrame->ExportVRML_File( aFullFileName, aMMtoWRMLunit,
  276. aExport3DFiles, aUseRelativePaths,
  277. a3D_Subdir, aXRef, aYRef );
  278. return ok;
  279. }
  280. else
  281. {
  282. return false;
  283. }
  284. }
  285. bool ImportSpecctraSES( wxString& aFullFilename )
  286. {
  287. if( s_PcbEditFrame )
  288. {
  289. bool ok = s_PcbEditFrame->ImportSpecctraSession( aFullFilename );
  290. return ok;
  291. }
  292. else
  293. {
  294. return false;
  295. }
  296. }
  297. bool ExportFootprintsToLibrary( bool aStoreInNewLib, const wxString& aLibName, wxString* aLibPath )
  298. {
  299. if( s_PcbEditFrame )
  300. {
  301. s_PcbEditFrame->ExportFootprintsToLibrary( aStoreInNewLib, aLibName, aLibPath );
  302. return true;
  303. }
  304. else
  305. {
  306. return false;
  307. }
  308. }
  309. void Refresh()
  310. {
  311. if( s_PcbEditFrame )
  312. {
  313. TOOL_MANAGER* toolMgr = s_PcbEditFrame->GetToolManager();
  314. BOARD* board = s_PcbEditFrame->GetBoard();
  315. PCB_DRAW_PANEL_GAL* canvas = s_PcbEditFrame->GetCanvas();
  316. canvas->SyncLayersVisibility( board );
  317. canvas->GetView()->Clear();
  318. canvas->GetView()->InitPreview();
  319. canvas->GetGAL()->SetGridOrigin( VECTOR2D( board->GetDesignSettings().GetGridOrigin() ) );
  320. canvas->DisplayBoard( board );
  321. // allow tools to re-add their view items (selection previews, grids, etc.)
  322. if( toolMgr )
  323. toolMgr->ResetTools( TOOL_BASE::GAL_SWITCH );
  324. // reload the drawing-sheet
  325. s_PcbEditFrame->SetPageSettings( board->GetPageSettings() );
  326. board->BuildConnectivity();
  327. canvas->Refresh();
  328. }
  329. }
  330. void UpdateUserInterface()
  331. {
  332. if( s_PcbEditFrame )
  333. s_PcbEditFrame->UpdateUserInterface();
  334. }
  335. int GetUserUnits()
  336. {
  337. if( s_PcbEditFrame )
  338. return static_cast<int>( s_PcbEditFrame->GetUserUnits() );
  339. return -1;
  340. }
  341. bool IsActionRunning()
  342. {
  343. return ACTION_PLUGINS::IsActionRunning();
  344. }
  345. bool WriteDRCReport( BOARD* aBoard, const wxString& aFileName, EDA_UNITS aUnits,
  346. bool aReportAllTrackErrors )
  347. {
  348. wxCHECK( aBoard, false );
  349. BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings();
  350. std::shared_ptr<DRC_ENGINE> engine = bds.m_DRCEngine;
  351. if( !engine )
  352. {
  353. bds.m_DRCEngine = std::make_shared<DRC_ENGINE>( aBoard, &bds );
  354. engine = bds.m_DRCEngine;
  355. }
  356. wxCHECK( engine, false );
  357. wxFileName fn = aBoard->GetFileName();
  358. fn.SetExt( DesignRulesFileExtension );
  359. PROJECT* prj = nullptr;
  360. if( aBoard->GetProject() )
  361. prj = aBoard->GetProject();
  362. else if( s_SettingsManager )
  363. prj = &s_SettingsManager->Prj();
  364. wxCHECK( prj, false );
  365. wxString drcRulesPath = prj->AbsolutePath( fn.GetFullName() );
  366. try
  367. {
  368. engine->InitEngine( drcRulesPath );
  369. }
  370. catch( PARSE_ERROR& )
  371. {
  372. return false;
  373. }
  374. std::vector<std::shared_ptr<DRC_ITEM>> footprints;
  375. std::vector<std::shared_ptr<DRC_ITEM>> unconnected;
  376. std::vector<std::shared_ptr<DRC_ITEM>> violations;
  377. engine->SetProgressReporter( nullptr );
  378. engine->SetViolationHandler(
  379. [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2D aPos, PCB_LAYER_ID aLayer )
  380. {
  381. if( aItem->GetErrorCode() == DRCE_MISSING_FOOTPRINT
  382. || aItem->GetErrorCode() == DRCE_DUPLICATE_FOOTPRINT
  383. || aItem->GetErrorCode() == DRCE_EXTRA_FOOTPRINT
  384. || aItem->GetErrorCode() == DRCE_NET_CONFLICT )
  385. {
  386. footprints.push_back( aItem );
  387. }
  388. else if( aItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
  389. {
  390. unconnected.push_back( aItem );
  391. }
  392. else
  393. {
  394. violations.push_back( aItem );
  395. }
  396. } );
  397. engine->RunTests( aUnits, aReportAllTrackErrors, false );
  398. engine->ClearViolationHandler();
  399. // TODO: Unify this with DIALOG_DRC::writeReport
  400. FILE* fp = wxFopen( aFileName, wxT( "w" ) );
  401. if( fp == nullptr )
  402. return false;
  403. std::map<KIID, EDA_ITEM*> itemMap;
  404. aBoard->FillItemMap( itemMap );
  405. fprintf( fp, "** Drc report for %s **\n", TO_UTF8( aBoard->GetFileName() ) );
  406. wxDateTime now = wxDateTime::Now();
  407. fprintf( fp, "** Created on %s **\n", TO_UTF8( now.Format( wxT( "%F %T" ) ) ) );
  408. fprintf( fp, "\n** Found %d DRC violations **\n", static_cast<int>( violations.size() ) );
  409. for( const std::shared_ptr<DRC_ITEM>& item : violations )
  410. {
  411. SEVERITY severity = item->GetParent()->GetSeverity();
  412. fprintf( fp, "%s", TO_UTF8( item->ShowReport( aUnits, severity, itemMap ) ) );
  413. }
  414. fprintf( fp, "\n** Found %d unconnected pads **\n", static_cast<int>( unconnected.size() ) );
  415. for( const std::shared_ptr<DRC_ITEM>& item : unconnected )
  416. {
  417. SEVERITY severity = bds.GetSeverity( item->GetErrorCode() );
  418. fprintf( fp, "%s", TO_UTF8( item->ShowReport( aUnits, severity, itemMap ) ) );
  419. }
  420. fprintf( fp, "\n** Found %d Footprint errors **\n", static_cast<int>( footprints.size() ) );
  421. for( const std::shared_ptr<DRC_ITEM>& item : footprints )
  422. {
  423. SEVERITY severity = bds.GetSeverity( item->GetErrorCode() );
  424. fprintf( fp, "%s", TO_UTF8( item->ShowReport( aUnits, severity, itemMap ) ) );
  425. }
  426. fprintf( fp, "\n** End of Report **\n" );
  427. fclose( fp );
  428. return true;
  429. }