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.

420 lines
11 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <bus_alias.h>
  20. #include <connection_graph.h>
  21. #include <erc_settings.h>
  22. #include <sch_marker.h>
  23. #include <project.h>
  24. #include <project/project_file.h>
  25. #include <project/net_settings.h>
  26. #include <schematic.h>
  27. #include <sch_screen.h>
  28. #include <sim/spice_settings.h>
  29. SCHEMATIC::SCHEMATIC( PROJECT* aPrj ) :
  30. EDA_ITEM( nullptr, SCHEMATIC_T ),
  31. m_project( nullptr ),
  32. m_rootSheet( nullptr )
  33. {
  34. m_currentSheet = new SCH_SHEET_PATH();
  35. m_connectionGraph = new CONNECTION_GRAPH( this );
  36. SetProject( aPrj );
  37. }
  38. SCHEMATIC::~SCHEMATIC()
  39. {
  40. delete m_currentSheet;
  41. delete m_connectionGraph;
  42. }
  43. void SCHEMATIC::Reset()
  44. {
  45. // Assume project already saved
  46. if( m_project )
  47. {
  48. PROJECT_FILE& project = m_project->GetProjectFile();
  49. delete project.m_ErcSettings;
  50. delete project.m_SchematicSettings;
  51. project.m_ErcSettings = nullptr;
  52. project.m_SchematicSettings = nullptr;
  53. m_project = nullptr; // clear the project, so we don't do this again when setting a new one
  54. }
  55. delete m_rootSheet;
  56. m_rootSheet = nullptr;
  57. m_connectionGraph->Reset();
  58. m_currentSheet->clear();
  59. }
  60. void SCHEMATIC::SetProject( PROJECT* aPrj )
  61. {
  62. if( m_project )
  63. {
  64. PROJECT_FILE& project = m_project->GetProjectFile();
  65. delete project.m_ErcSettings;
  66. delete project.m_SchematicSettings;
  67. project.m_ErcSettings = nullptr;
  68. project.m_SchematicSettings = nullptr;
  69. }
  70. m_project = aPrj;
  71. if( m_project )
  72. {
  73. PROJECT_FILE& project = m_project->GetProjectFile();
  74. project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
  75. project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
  76. project.m_SchematicSettings->LoadFromFile();
  77. project.m_SchematicSettings->m_NgspiceSimulatorSettings->LoadFromFile();
  78. project.m_ErcSettings->LoadFromFile();
  79. }
  80. }
  81. void SCHEMATIC::SetRoot( SCH_SHEET* aRootSheet )
  82. {
  83. wxCHECK_RET( aRootSheet, "Call to SetRoot with null SCH_SHEET!" );
  84. m_rootSheet = aRootSheet;
  85. m_currentSheet->clear();
  86. m_currentSheet->push_back( m_rootSheet );
  87. m_connectionGraph->Reset();
  88. }
  89. SCH_SCREEN* SCHEMATIC::RootScreen() const
  90. {
  91. return IsValid() ? m_rootSheet->GetScreen() : nullptr;
  92. }
  93. bool SCHEMATIC::ResolveTextVar( wxString* token, int aDepth ) const
  94. {
  95. if( !CurrentSheet().empty() )
  96. {
  97. if( token->IsSameAs( wxT( "#" ) ) )
  98. {
  99. *token = CurrentSheet().GetPageNumber();
  100. return true;
  101. }
  102. else if( token->IsSameAs( wxT( "##" ) ) )
  103. {
  104. *token = wxString::Format( "%i", Root().CountSheets() );
  105. return true;
  106. }
  107. else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
  108. {
  109. *token = CurrentSheet().PathHumanReadable();
  110. return true;
  111. }
  112. else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
  113. {
  114. *token = CurrentSheet().Last()->GetName();
  115. return true;
  116. }
  117. else if( token->IsSameAs( wxT( "FILENAME" ) ) )
  118. {
  119. wxFileName fn( GetFileName() );
  120. *token = fn.GetFullName();
  121. return true;
  122. }
  123. else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
  124. {
  125. *token = Prj().GetProjectName();
  126. return true;
  127. }
  128. return CurrentSheet().LastScreen()->GetTitleBlock().TextVarResolver( token, m_project );
  129. }
  130. return false;
  131. }
  132. wxString SCHEMATIC::GetFileName() const
  133. {
  134. return IsValid() ? m_rootSheet->GetScreen()->GetFileName() : wxString( wxEmptyString );
  135. }
  136. SCHEMATIC_SETTINGS& SCHEMATIC::Settings() const
  137. {
  138. wxASSERT( m_project );
  139. return *m_project->GetProjectFile().m_SchematicSettings;
  140. }
  141. ERC_SETTINGS& SCHEMATIC::ErcSettings() const
  142. {
  143. wxASSERT( m_project );
  144. return *m_project->GetProjectFile().m_ErcSettings;
  145. }
  146. std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
  147. {
  148. SCH_SHEET_LIST sheetList = GetSheets();
  149. ERC_SETTINGS& settings = ErcSettings();
  150. for( const SCH_SHEET_PATH& sheet : sheetList )
  151. {
  152. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
  153. {
  154. SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
  155. auto it = settings.m_ErcExclusions.find( marker->Serialize() );
  156. if( it != settings.m_ErcExclusions.end() )
  157. {
  158. marker->SetExcluded( true );
  159. settings.m_ErcExclusions.erase( it );
  160. }
  161. }
  162. }
  163. std::vector<SCH_MARKER*> newMarkers;
  164. for( const wxString& exclusionData : settings.m_ErcExclusions )
  165. {
  166. SCH_MARKER* marker = SCH_MARKER::Deserialize( exclusionData );
  167. if( marker )
  168. {
  169. marker->SetExcluded( true );
  170. newMarkers.push_back( marker );
  171. }
  172. }
  173. settings.m_ErcExclusions.clear();
  174. return newMarkers;
  175. }
  176. std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
  177. {
  178. for( const auto& sheet : GetSheets() )
  179. {
  180. for( const auto& alias : sheet.LastScreen()->GetBusAliases() )
  181. {
  182. if( alias->GetName() == aLabel )
  183. return alias;
  184. }
  185. }
  186. return nullptr;
  187. }
  188. std::vector<wxString> SCHEMATIC::GetNetClassAssignmentCandidates()
  189. {
  190. std::vector<wxString> names;
  191. // Key is a NET_NAME_CODE aka std::pair<name, code>
  192. for( const NET_MAP::value_type& pair: m_connectionGraph->GetNetMap() )
  193. {
  194. CONNECTION_SUBGRAPH* subgraph = pair.second[0];
  195. if( !subgraph->m_driver_connection->IsBus()
  196. && subgraph->GetDriverPriority() >= CONNECTION_SUBGRAPH::PRIORITY::PIN )
  197. {
  198. names.emplace_back( pair.first.first );
  199. }
  200. }
  201. return names;
  202. }
  203. bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
  204. {
  205. SCH_SHEET_LIST sheetList = GetSheets();
  206. wxString remainder;
  207. wxString ref = token->BeforeFirst( ':', &remainder );
  208. SCH_SHEET_PATH sheetPath;
  209. SCH_ITEM* refItem = sheetList.GetItem( KIID( ref ), &sheetPath );
  210. if( refItem && refItem->Type() == SCH_SYMBOL_T )
  211. {
  212. SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
  213. if( refSymbol->ResolveTextVar( &remainder, aDepth + 1 ) )
  214. *token = remainder;
  215. else
  216. *token = refSymbol->GetRef( &sheetPath, true ) + ":" + remainder;
  217. return true; // Cross-reference is resolved whether or not the actual textvar was
  218. }
  219. else if( refItem && refItem->Type() == SCH_SHEET_T )
  220. {
  221. SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
  222. if( refSheet->ResolveTextVar( &remainder, aDepth + 1 ) )
  223. *token = remainder;
  224. return true; // Cross-reference is resolved whether or not the actual textvar was
  225. }
  226. return false;
  227. }
  228. wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
  229. {
  230. wxString newbuf;
  231. size_t sourceLen = aSource.length();
  232. for( size_t i = 0; i < sourceLen; ++i )
  233. {
  234. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  235. {
  236. wxString token;
  237. bool isCrossRef = false;
  238. int nesting = 0;
  239. for( i = i + 2; i < sourceLen; ++i )
  240. {
  241. if( aSource[i] == '{'
  242. && ( aSource[i-1] == '_' || aSource[i-1] == '^' || aSource[i-1] == '~' ) )
  243. {
  244. nesting++;
  245. }
  246. if( aSource[i] == '}' )
  247. {
  248. nesting--;
  249. if( nesting < 0 )
  250. break;
  251. }
  252. if( aSource[i] == ':' )
  253. isCrossRef = true;
  254. token.append( aSource[i] );
  255. }
  256. if( isCrossRef )
  257. {
  258. SCH_SHEET_LIST sheetList = GetSheets();
  259. wxString remainder;
  260. wxString ref = token.BeforeFirst( ':', &remainder );
  261. SCH_REFERENCE_LIST references;
  262. sheetList.GetSymbols( references );
  263. for( size_t jj = 0; jj < references.GetCount(); jj++ )
  264. {
  265. SCH_SYMBOL* refSymbol = references[ jj ].GetSymbol();
  266. if( ref == refSymbol->GetRef( &references[ jj ].GetSheetPath(), true ) )
  267. {
  268. token = refSymbol->m_Uuid.AsString() + ":" + remainder;
  269. break;
  270. }
  271. }
  272. }
  273. newbuf.append( "${" + token + "}" );
  274. }
  275. else
  276. {
  277. newbuf.append( aSource[i] );
  278. }
  279. }
  280. return newbuf;
  281. }
  282. wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
  283. {
  284. wxString newbuf;
  285. size_t sourceLen = aSource.length();
  286. for( size_t i = 0; i < sourceLen; ++i )
  287. {
  288. if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
  289. {
  290. wxString token;
  291. bool isCrossRef = false;
  292. for( i = i + 2; i < sourceLen; ++i )
  293. {
  294. if( aSource[i] == '}' )
  295. break;
  296. if( aSource[i] == ':' )
  297. isCrossRef = true;
  298. token.append( aSource[i] );
  299. }
  300. if( isCrossRef )
  301. {
  302. SCH_SHEET_LIST sheetList = GetSheets();
  303. wxString remainder;
  304. wxString ref = token.BeforeFirst( ':', &remainder );
  305. SCH_SHEET_PATH refSheetPath;
  306. SCH_ITEM* refItem = sheetList.GetItem( KIID( ref ), &refSheetPath );
  307. if( refItem && refItem->Type() == SCH_SYMBOL_T )
  308. {
  309. SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
  310. token = refSymbol->GetRef( &refSheetPath, true ) + ":" + remainder;
  311. }
  312. }
  313. newbuf.append( "${" + token + "}" );
  314. }
  315. else
  316. {
  317. newbuf.append( aSource[i] );
  318. }
  319. }
  320. return newbuf;
  321. }
  322. SCH_SHEET_LIST& SCHEMATIC::GetFullHierarchy() const
  323. {
  324. static SCH_SHEET_LIST hierarchy;
  325. hierarchy.clear();
  326. hierarchy.BuildSheetList( m_rootSheet, false );
  327. return hierarchy;
  328. }