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.

1236 lines
36 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 CERN
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Jon Evans <jon@craftyjon.com>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <algorithm>
  22. #include <limits>
  23. #include <nlohmann/json.hpp>
  24. #include <project/net_settings.h>
  25. #include <settings/parameters.h>
  26. #include <settings/settings_manager.h>
  27. #include <string_utils.h>
  28. #include <base_units.h>
  29. // const int netSettingsSchemaVersion = 0;
  30. // const int netSettingsSchemaVersion = 1; // new overbar syntax
  31. // const int netSettingsSchemaVersion = 2; // exclude buses from netclass members
  32. // const int netSettingsSchemaVersion = 3; // netclass assignment patterns
  33. const int netSettingsSchemaVersion = 4; // netclass ordering
  34. static std::optional<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
  35. std::optional<int> aDefault = std::optional<int>() )
  36. {
  37. if( aObj.contains( aKey ) && aObj[aKey].is_number() )
  38. return pcbIUScale.mmToIU( aObj[aKey].get<double>() );
  39. else
  40. return aDefault;
  41. };
  42. static std::optional<int> getInSchUnits( const nlohmann::json& aObj, const std::string& aKey,
  43. std::optional<int> aDefault = std::optional<int>() )
  44. {
  45. if( aObj.contains( aKey ) && aObj[aKey].is_number() )
  46. return schIUScale.MilsToIU( aObj[aKey].get<double>() );
  47. else
  48. return aDefault;
  49. };
  50. NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
  51. NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath, false )
  52. {
  53. m_defaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default, true );
  54. m_defaultNetClass->SetDescription( _( "This is the default net class." ) );
  55. m_defaultNetClass->SetPriority( std::numeric_limits<int>::max() );
  56. auto saveNetclass =
  57. []( nlohmann::json& json_array, const std::shared_ptr<NETCLASS>& nc )
  58. {
  59. // Note: we're in common/, but we do happen to know which of these
  60. // fields are used in which units system.
  61. nlohmann::json nc_json = { { "name", nc->GetName().ToUTF8() },
  62. { "priority", nc->GetPriority() },
  63. { "schematic_color", nc->GetSchematicColor( true ) },
  64. { "pcb_color", nc->GetPcbColor( true ) } };
  65. auto saveInPcbUnits =
  66. []( nlohmann::json& json, const std::string& aKey, int aValue )
  67. {
  68. json.push_back( { aKey, pcbIUScale.IUTomm( aValue ) } );
  69. };
  70. if( nc->HasWireWidth() )
  71. nc_json.push_back(
  72. { "wire_width", schIUScale.IUToMils( nc->GetWireWidth() ) } );
  73. if( nc->HasBusWidth() )
  74. nc_json.push_back( { "bus_width", schIUScale.IUToMils( nc->GetBusWidth() ) } );
  75. if( nc->HasLineStyle() )
  76. nc_json.push_back( { "line_style", nc->GetLineStyle() } );
  77. if( nc->HasClearance() )
  78. saveInPcbUnits( nc_json, "clearance", nc->GetClearance() );
  79. if( nc->HasTrackWidth() )
  80. saveInPcbUnits( nc_json, "track_width", nc->GetTrackWidth() );
  81. if( nc->HasViaDiameter() )
  82. saveInPcbUnits( nc_json, "via_diameter", nc->GetViaDiameter() );
  83. if( nc->HasViaDrill() )
  84. saveInPcbUnits( nc_json, "via_drill", nc->GetViaDrill() );
  85. if( nc->HasuViaDiameter() )
  86. saveInPcbUnits( nc_json, "microvia_diameter", nc->GetuViaDiameter() );
  87. if( nc->HasuViaDrill() )
  88. saveInPcbUnits( nc_json, "microvia_drill", nc->GetuViaDrill() );
  89. if( nc->HasDiffPairWidth() )
  90. saveInPcbUnits( nc_json, "diff_pair_width", nc->GetDiffPairWidth() );
  91. if( nc->HasDiffPairGap() )
  92. saveInPcbUnits( nc_json, "diff_pair_gap", nc->GetDiffPairGap() );
  93. if( nc->HasDiffPairViaGap() )
  94. saveInPcbUnits( nc_json, "diff_pair_via_gap", nc->GetDiffPairViaGap() );
  95. json_array.push_back( nc_json );
  96. };
  97. auto readNetClass =
  98. []( const nlohmann::json& entry )
  99. {
  100. wxString name = entry["name"];
  101. std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( name, false );
  102. int priority = entry["priority"];
  103. nc->SetPriority( priority );
  104. if( auto value = getInPcbUnits( entry, "clearance" ) )
  105. nc->SetClearance( *value );
  106. if( auto value = getInPcbUnits( entry, "track_width" ) )
  107. nc->SetTrackWidth( *value );
  108. if( auto value = getInPcbUnits( entry, "via_diameter" ) )
  109. nc->SetViaDiameter( *value );
  110. if( auto value = getInPcbUnits( entry, "via_drill" ) )
  111. nc->SetViaDrill( *value );
  112. if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
  113. nc->SetuViaDiameter( *value );
  114. if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
  115. nc->SetuViaDrill( *value );
  116. if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
  117. nc->SetDiffPairWidth( *value );
  118. if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
  119. nc->SetDiffPairGap( *value );
  120. if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
  121. nc->SetDiffPairViaGap( *value );
  122. if( auto value = getInSchUnits( entry, "wire_width" ) )
  123. nc->SetWireWidth( *value );
  124. if( auto value = getInSchUnits( entry, "bus_width" ) )
  125. nc->SetBusWidth( *value );
  126. if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
  127. nc->SetLineStyle( entry["line_style"].get<int>() );
  128. if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
  129. nc->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
  130. if( entry.contains( "schematic_color" )
  131. && entry["schematic_color"].is_string() )
  132. {
  133. nc->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
  134. }
  135. return nc;
  136. };
  137. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
  138. [&]() -> nlohmann::json
  139. {
  140. nlohmann::json ret = nlohmann::json::array();
  141. if( m_defaultNetClass )
  142. saveNetclass( ret, m_defaultNetClass );
  143. for( const auto& [name, netclass] : m_netClasses )
  144. saveNetclass( ret, netclass );
  145. return ret;
  146. },
  147. [&]( const nlohmann::json& aJson )
  148. {
  149. if( !aJson.is_array() )
  150. return;
  151. m_netClasses.clear();
  152. for( const nlohmann::json& entry : aJson )
  153. {
  154. if( !entry.is_object() || !entry.contains( "name" ) )
  155. continue;
  156. std::shared_ptr<NETCLASS> nc = readNetClass( entry );
  157. if( nc->IsDefault() )
  158. m_defaultNetClass = nc;
  159. else
  160. m_netClasses[nc->GetName()] = nc;
  161. }
  162. },
  163. {} ) );
  164. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
  165. [&]() -> nlohmann::json
  166. {
  167. nlohmann::json ret = {};
  168. for( const auto& [netname, color] : m_netColorAssignments )
  169. {
  170. std::string key( netname.ToUTF8() );
  171. ret[ std::move( key ) ] = color;
  172. }
  173. return ret;
  174. },
  175. [&]( const nlohmann::json& aJson )
  176. {
  177. if( !aJson.is_object() )
  178. return;
  179. m_netColorAssignments.clear();
  180. for( const auto& pair : aJson.items() )
  181. {
  182. wxString key( pair.key().c_str(), wxConvUTF8 );
  183. m_netColorAssignments[std::move( key )] = pair.value().get<KIGFX::COLOR4D>();
  184. }
  185. },
  186. {} ) );
  187. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
  188. [&]() -> nlohmann::json
  189. {
  190. nlohmann::json ret = {};
  191. for( const auto& [netname, netclassNames] : m_netClassLabelAssignments )
  192. {
  193. nlohmann::json netclassesJson = nlohmann::json::array();
  194. for( const auto& netclass : netclassNames )
  195. {
  196. std::string netclassStr( netclass.ToUTF8() );
  197. netclassesJson.push_back( std::move( netclassStr ) );
  198. }
  199. std::string key( netname.ToUTF8() );
  200. ret[std::move( key )] = netclassesJson;
  201. }
  202. return ret;
  203. },
  204. [&]( const nlohmann::json& aJson )
  205. {
  206. if( !aJson.is_object() )
  207. return;
  208. m_netClassLabelAssignments.clear();
  209. for( const auto& pair : aJson.items() )
  210. {
  211. wxString key( pair.key().c_str(), wxConvUTF8 );
  212. for( const auto& netclassName : pair.value() )
  213. m_netClassLabelAssignments[key].insert( netclassName.get<wxString>() );
  214. }
  215. },
  216. {} ) );
  217. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
  218. [&]() -> nlohmann::json
  219. {
  220. nlohmann::json ret = nlohmann::json::array();
  221. for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
  222. {
  223. nlohmann::json pattern_json = {
  224. { "pattern", matcher->GetPattern().ToUTF8() },
  225. { "netclass", netclassName.ToUTF8() }
  226. };
  227. ret.push_back( std::move( pattern_json ) );
  228. }
  229. return ret;
  230. },
  231. [&]( const nlohmann::json& aJson )
  232. {
  233. if( !aJson.is_array() )
  234. return;
  235. m_netClassPatternAssignments.clear();
  236. for( const nlohmann::json& entry : aJson )
  237. {
  238. if( !entry.is_object() )
  239. continue;
  240. if( entry.contains( "pattern" ) && entry["pattern"].is_string()
  241. && entry.contains( "netclass" ) && entry["netclass"].is_string() )
  242. {
  243. wxString pattern = entry["pattern"].get<wxString>();
  244. wxString netclass = entry["netclass"].get<wxString>();
  245. m_netClassPatternAssignments.push_back(
  246. { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ),
  247. netclass } );
  248. }
  249. }
  250. },
  251. {} ) );
  252. registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
  253. registerMigration( 1, 2, std::bind( &NET_SETTINGS::migrateSchema1to2, this ) );
  254. registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
  255. registerMigration( 3, 4, std::bind( &NET_SETTINGS::migrateSchema3to4, this ) );
  256. }
  257. NET_SETTINGS::~NET_SETTINGS()
  258. {
  259. // Release early before destroying members
  260. if( m_parent )
  261. {
  262. m_parent->ReleaseNestedSettings( this );
  263. m_parent = nullptr;
  264. }
  265. }
  266. bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
  267. {
  268. if( !std::equal( std::begin( m_netClasses ), std::end( m_netClasses ),
  269. std::begin( aOther.m_netClasses ) ) )
  270. return false;
  271. if( !std::equal( std::begin( m_netClassPatternAssignments ),
  272. std::end( m_netClassPatternAssignments ),
  273. std::begin( aOther.m_netClassPatternAssignments ) ) )
  274. return false;
  275. if( !std::equal( std::begin( m_netClassLabelAssignments ),
  276. std::end( m_netClassLabelAssignments ),
  277. std::begin( aOther.m_netClassLabelAssignments ) ) )
  278. return false;
  279. if( !std::equal( std::begin( m_netColorAssignments ), std::end( m_netColorAssignments ),
  280. std::begin( aOther.m_netColorAssignments ) ) )
  281. return false;
  282. return true;
  283. }
  284. bool NET_SETTINGS::migrateSchema0to1()
  285. {
  286. if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
  287. {
  288. for( auto& netClass : m_internals->At( "classes" ).items() )
  289. {
  290. if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
  291. {
  292. nlohmann::json migrated = nlohmann::json::array();
  293. for( auto& net : netClass.value()["nets"].items() )
  294. migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
  295. netClass.value()["nets"] = migrated;
  296. }
  297. }
  298. }
  299. return true;
  300. }
  301. bool NET_SETTINGS::migrateSchema1to2()
  302. {
  303. return true;
  304. }
  305. bool NET_SETTINGS::migrateSchema2to3()
  306. {
  307. if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
  308. {
  309. nlohmann::json patterns = nlohmann::json::array();
  310. for( auto& netClass : m_internals->At( "classes" ).items() )
  311. {
  312. if( netClass.value().contains( "name" )
  313. && netClass.value().contains( "nets" )
  314. && netClass.value()["nets"].is_array() )
  315. {
  316. wxString netClassName = netClass.value()["name"].get<wxString>();
  317. for( auto& net : netClass.value()["nets"].items() )
  318. {
  319. nlohmann::json pattern_json = {
  320. { "pattern", net.value().get<wxString>() },
  321. { "netclass", netClassName }
  322. };
  323. patterns.push_back( pattern_json );
  324. }
  325. }
  326. }
  327. m_internals->SetFromString( "netclass_patterns", patterns );
  328. }
  329. return true;
  330. }
  331. bool NET_SETTINGS::migrateSchema3to4()
  332. {
  333. // Add priority field to netclasses
  334. if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
  335. {
  336. int priority = 0;
  337. for( auto& netClass : m_internals->At( "classes" ).items() )
  338. {
  339. if( netClass.value()["name"].get<wxString>() == NETCLASS::Default )
  340. netClass.value()["priority"] = std::numeric_limits<int>::max();
  341. else
  342. netClass.value()["priority"] = priority++;
  343. }
  344. }
  345. // Move netclass assignments to a list
  346. if( m_internals->contains( "netclass_assignments" )
  347. && m_internals->At( "netclass_assignments" ).is_object() )
  348. {
  349. nlohmann::json migrated = {};
  350. for( const auto& pair : m_internals->At( "netclass_assignments" ).items() )
  351. {
  352. nlohmann::json netclassesJson = nlohmann::json::array();
  353. if( pair.value().get<wxString>() != wxEmptyString )
  354. netclassesJson.push_back( pair.value() );
  355. migrated[pair.key()] = netclassesJson;
  356. }
  357. m_internals->SetFromString( "netclass_assignments", migrated );
  358. }
  359. return true;
  360. }
  361. void NET_SETTINGS::SetDefaultNetclass( std::shared_ptr<NETCLASS> netclass )
  362. {
  363. m_defaultNetClass = netclass;
  364. }
  365. std::shared_ptr<NETCLASS> NET_SETTINGS::GetDefaultNetclass()
  366. {
  367. return m_defaultNetClass;
  368. }
  369. bool NET_SETTINGS::HasNetclass( const wxString& netclassName ) const
  370. {
  371. return m_netClasses.find( netclassName ) != m_netClasses.end();
  372. }
  373. void NET_SETTINGS::SetNetclass( const wxString& netclassName, std::shared_ptr<NETCLASS>& netclass )
  374. {
  375. m_netClasses[netclassName] = netclass;
  376. }
  377. void NET_SETTINGS::SetNetclasses( const std::map<wxString, std::shared_ptr<NETCLASS>>& netclasses )
  378. {
  379. m_netClasses = netclasses;
  380. ClearAllCaches();
  381. }
  382. const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetNetclasses() const
  383. {
  384. return m_netClasses;
  385. }
  386. const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetCompositeNetclasses() const
  387. {
  388. return m_compositeNetClasses;
  389. }
  390. void NET_SETTINGS::ClearNetclasses()
  391. {
  392. m_netClasses.clear();
  393. m_impicitNetClasses.clear();
  394. ClearAllCaches();
  395. }
  396. const std::map<wxString, std::set<wxString>>& NET_SETTINGS::GetNetclassLabelAssignments() const
  397. {
  398. return m_netClassLabelAssignments;
  399. }
  400. void NET_SETTINGS::ClearNetclassLabelAssignments()
  401. {
  402. m_netClassLabelAssignments.clear();
  403. }
  404. void NET_SETTINGS::ClearNetclassLabelAssignment( const wxString& netName )
  405. {
  406. m_netClassLabelAssignments.erase( netName );
  407. }
  408. void NET_SETTINGS::SetNetclassLabelAssignment( const wxString& netName,
  409. const std::set<wxString>& netclasses )
  410. {
  411. m_netClassLabelAssignments[netName] = netclasses;
  412. }
  413. void NET_SETTINGS::AppendNetclassLabelAssignment( const wxString& netName,
  414. const std::set<wxString>& netclasses )
  415. {
  416. m_netClassLabelAssignments[netName].insert( netclasses.begin(), netclasses.end() );
  417. }
  418. bool NET_SETTINGS::HasNetclassLabelAssignment( const wxString& netName ) const
  419. {
  420. return m_netClassLabelAssignments.find( netName ) != m_netClassLabelAssignments.end();
  421. }
  422. void NET_SETTINGS::SetNetclassPatternAssignment( const wxString& pattern, const wxString& netclass )
  423. {
  424. // Replace existing assignment if we have one
  425. for( auto& assignment : m_netClassPatternAssignments )
  426. {
  427. if( assignment.first->GetPattern() == pattern )
  428. {
  429. assignment.second = netclass;
  430. ClearAllCaches();
  431. return;
  432. }
  433. }
  434. // No assignment, add a new one
  435. m_netClassPatternAssignments.push_back(
  436. { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
  437. ClearAllCaches();
  438. }
  439. void NET_SETTINGS::SetNetclassPatternAssignments(
  440. std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&& netclassPatterns )
  441. {
  442. m_netClassPatternAssignments = std::move( netclassPatterns );
  443. ClearAllCaches();
  444. }
  445. std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
  446. NET_SETTINGS::GetNetclassPatternAssignments()
  447. {
  448. return m_netClassPatternAssignments;
  449. }
  450. void NET_SETTINGS::ClearNetclassPatternAssignments()
  451. {
  452. m_netClassPatternAssignments.clear();
  453. }
  454. void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
  455. {
  456. if( m_effectiveNetclassCache.count( netName ) )
  457. {
  458. wxString compositeNetclassName = m_effectiveNetclassCache[netName]->GetName();
  459. m_compositeNetClasses.erase( compositeNetclassName );
  460. m_effectiveNetclassCache.erase( netName );
  461. }
  462. }
  463. void NET_SETTINGS::ClearAllCaches()
  464. {
  465. m_effectiveNetclassCache.clear();
  466. m_compositeNetClasses.clear();
  467. }
  468. void NET_SETTINGS::SetNetColorAssignment( const wxString& netName, const KIGFX::COLOR4D& color )
  469. {
  470. m_netColorAssignments[netName] = color;
  471. }
  472. const std::map<wxString, KIGFX::COLOR4D>& NET_SETTINGS::GetNetColorAssignments() const
  473. {
  474. return m_netColorAssignments;
  475. }
  476. void NET_SETTINGS::ClearNetColorAssignments()
  477. {
  478. m_netColorAssignments.clear();
  479. }
  480. bool NET_SETTINGS::HasEffectiveNetClass( const wxString& aNetName ) const
  481. {
  482. return m_effectiveNetclassCache.count( aNetName ) > 0;
  483. }
  484. std::shared_ptr<NETCLASS> NET_SETTINGS::GetCachedEffectiveNetClass( const wxString& aNetName ) const
  485. {
  486. return m_effectiveNetclassCache.at( aNetName );
  487. }
  488. std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName )
  489. {
  490. // Lambda to fetch an explicit netclass. Returns a nullptr if not found
  491. auto getExplicitNetclass = [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
  492. {
  493. if( netclass == NETCLASS::Default )
  494. return m_defaultNetClass;
  495. auto ii = m_netClasses.find( netclass );
  496. if( ii == m_netClasses.end() )
  497. return {};
  498. else
  499. return ii->second;
  500. };
  501. // Lambda to fetch or create an implicit netclass (defined with a label, but not configured)
  502. // These are needed as while they do not provide any netclass parameters, they do now appear in
  503. // DRC matching strings as an assigned netclass.
  504. auto getOrAddImplicitNetcless = [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
  505. {
  506. auto ii = m_impicitNetClasses.find( netclass );
  507. if( ii == m_impicitNetClasses.end() )
  508. {
  509. std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( netclass, false );
  510. nc->SetPriority( std::numeric_limits<int>::max() - 1 ); // Priority > default netclass
  511. m_impicitNetClasses[netclass] = nc;
  512. return nc;
  513. }
  514. else
  515. {
  516. return ii->second;
  517. }
  518. };
  519. // <no net> is forced to be part of the default netclass.
  520. if( aNetName.IsEmpty() )
  521. return m_defaultNetClass;
  522. // First check if we have a cached resolved netclass
  523. auto cacheItr = m_effectiveNetclassCache.find( aNetName );
  524. if( cacheItr != m_effectiveNetclassCache.end() )
  525. return cacheItr->second;
  526. // No cache found - build a vector of all netclasses assigned to or matching this net
  527. std::vector<std::shared_ptr<NETCLASS>> resolvedNetclasses;
  528. // First find explicit netclass assignments
  529. auto it = m_netClassLabelAssignments.find( aNetName );
  530. if( it != m_netClassLabelAssignments.end() && it->second.size() > 0 )
  531. {
  532. for( const wxString& netclassName : it->second )
  533. {
  534. std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
  535. if( netclass )
  536. {
  537. resolvedNetclasses.push_back( std::move( netclass ) );
  538. }
  539. else
  540. {
  541. resolvedNetclasses.push_back(
  542. getOrAddImplicitNetcless( netclassName ) );
  543. }
  544. }
  545. }
  546. // Now find any pattern-matched netclass assignments
  547. for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
  548. {
  549. if( matcher->StartsWith( aNetName ) )
  550. {
  551. std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
  552. if( netclass )
  553. {
  554. resolvedNetclasses.push_back( std::move( netclass ) );
  555. }
  556. else
  557. {
  558. resolvedNetclasses.push_back( getOrAddImplicitNetcless( netclassName ) );
  559. }
  560. }
  561. }
  562. // Handle zero resolved netclasses
  563. if( resolvedNetclasses.size() == 0 )
  564. {
  565. m_effectiveNetclassCache[aNetName] = m_defaultNetClass;
  566. return m_defaultNetClass;
  567. }
  568. // Make and cache the effective netclass. Note that makeEffectiveNetclass will add the default
  569. // netclass to resolvedNetclasses if it is needed to complete the netclass paramters set. It
  570. // will also sort resolvedNetclasses by priority order.
  571. std::vector<NETCLASS*> netclassPtrs;
  572. for( std::shared_ptr<NETCLASS>& nc : resolvedNetclasses )
  573. netclassPtrs.push_back( nc.get() );
  574. wxString name;
  575. name.Printf( "Effective for net: %s", aNetName );
  576. std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
  577. makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
  578. if( netclassPtrs.size() == 1 )
  579. {
  580. // No defaults were added - just return the primary netclass
  581. m_effectiveNetclassCache[aNetName] = resolvedNetclasses[0];
  582. return resolvedNetclasses[0];
  583. }
  584. else
  585. {
  586. effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
  587. m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
  588. m_effectiveNetclassCache[aNetName] = effectiveNetclass;
  589. return effectiveNetclass;
  590. }
  591. }
  592. void NET_SETTINGS::RecomputeEffectiveNetclasses()
  593. {
  594. for( auto& [ncName, nc] : m_compositeNetClasses )
  595. {
  596. // Note this needs to be a copy in case we now need to add the default netclass
  597. std::vector<NETCLASS*> constituents = nc->GetConstituentNetclasses();
  598. wxASSERT( constituents.size() > 0 );
  599. // If the last netclass is Default, remove it (it will be re-added if still needed)
  600. if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
  601. {
  602. constituents.pop_back();
  603. }
  604. // Remake the netclass from original constituents
  605. nc->ResetParameters();
  606. makeEffectiveNetclass( nc, constituents );
  607. nc->SetConstituentNetclasses( std::move( constituents ) );
  608. }
  609. }
  610. void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
  611. std::vector<NETCLASS*>& constituentNetclasses ) const
  612. {
  613. // Sort the resolved netclasses by priority (highest first), with same-priority netclasses
  614. // ordered alphabetically
  615. std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
  616. []( NETCLASS* nc1, NETCLASS* nc2 )
  617. {
  618. int p1 = nc1->GetPriority();
  619. int p2 = nc2->GetPriority();
  620. if( p1 < p2 )
  621. return true;
  622. if (p1 == p2)
  623. return nc1->GetName().Cmp( nc2->GetName() ) < 0;
  624. return false;
  625. } );
  626. // Iterate from lowest priority netclass and fill effective netclass parameters
  627. for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
  628. {
  629. NETCLASS* nc = *itr;
  630. if( nc->HasClearance() )
  631. {
  632. effectiveNetclass->SetClearance( nc->GetClearance() );
  633. effectiveNetclass->SetClearanceParent( nc );
  634. }
  635. if( nc->HasTrackWidth() )
  636. {
  637. effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
  638. effectiveNetclass->SetTrackWidthParent( nc );
  639. }
  640. if( nc->HasViaDiameter() )
  641. {
  642. effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
  643. effectiveNetclass->SetViaDiameterParent( nc );
  644. }
  645. if( nc->HasViaDrill() )
  646. {
  647. effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
  648. effectiveNetclass->SetViaDrillParent( nc );
  649. }
  650. if( nc->HasuViaDiameter() )
  651. {
  652. effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
  653. effectiveNetclass->SetuViaDiameterParent( nc );
  654. }
  655. if( nc->HasuViaDrill() )
  656. {
  657. effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
  658. effectiveNetclass->SetuViaDrillParent( nc );
  659. }
  660. if( nc->HasDiffPairWidth() )
  661. {
  662. effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
  663. effectiveNetclass->SetDiffPairWidthParent( nc );
  664. }
  665. if( nc->HasDiffPairGap() )
  666. {
  667. effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
  668. effectiveNetclass->SetDiffPairGapParent( nc );
  669. }
  670. if( nc->HasDiffPairViaGap() )
  671. {
  672. effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
  673. effectiveNetclass->SetDiffPairViaGapParent( nc );
  674. }
  675. if( nc->HasWireWidth() )
  676. {
  677. effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
  678. effectiveNetclass->SetWireWidthParent( nc );
  679. }
  680. if( nc->HasBusWidth() )
  681. {
  682. effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
  683. effectiveNetclass->SetBusWidthParent( nc );
  684. }
  685. if( nc->HasLineStyle() )
  686. {
  687. effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
  688. effectiveNetclass->SetLineStyleParent( nc );
  689. }
  690. COLOR4D pcbColor = nc->GetPcbColor();
  691. if( pcbColor != COLOR4D::UNSPECIFIED )
  692. {
  693. effectiveNetclass->SetPcbColor( pcbColor );
  694. effectiveNetclass->SetPcbColorParent( nc );
  695. }
  696. COLOR4D schColor = nc->GetSchematicColor();
  697. if( schColor != COLOR4D::UNSPECIFIED )
  698. {
  699. effectiveNetclass->SetSchematicColor( schColor );
  700. effectiveNetclass->SetSchematicColorParent( nc );
  701. }
  702. }
  703. // Fill in any required defaults
  704. if( addMissingDefaults( effectiveNetclass.get() ) )
  705. constituentNetclasses.push_back( m_defaultNetClass.get() );
  706. }
  707. bool NET_SETTINGS::addMissingDefaults( NETCLASS* nc ) const
  708. {
  709. bool addedDefault = false;
  710. if( !nc->HasClearance() )
  711. {
  712. addedDefault = true;
  713. nc->SetClearance( m_defaultNetClass->GetClearance() );
  714. nc->SetClearanceParent( m_defaultNetClass.get() );
  715. }
  716. if( !nc->HasTrackWidth() )
  717. {
  718. addedDefault = true;
  719. nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
  720. nc->SetTrackWidthParent( m_defaultNetClass.get() );
  721. }
  722. if( !nc->HasViaDiameter() )
  723. {
  724. addedDefault = true;
  725. nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
  726. nc->SetViaDiameterParent( m_defaultNetClass.get() );
  727. }
  728. if( !nc->HasViaDrill() )
  729. {
  730. addedDefault = true;
  731. nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
  732. nc->SetViaDrillParent( m_defaultNetClass.get() );
  733. }
  734. if( !nc->HasuViaDiameter() )
  735. {
  736. addedDefault = true;
  737. nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
  738. nc->SetuViaDiameterParent( m_defaultNetClass.get() );
  739. }
  740. if( !nc->HasuViaDrill() )
  741. {
  742. addedDefault = true;
  743. nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
  744. nc->SetuViaDrillParent( m_defaultNetClass.get() );
  745. }
  746. if( !nc->HasDiffPairWidth() )
  747. {
  748. addedDefault = true;
  749. nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
  750. nc->SetDiffPairWidthParent( m_defaultNetClass.get() );
  751. }
  752. if( !nc->HasDiffPairGap() )
  753. {
  754. addedDefault = true;
  755. nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
  756. nc->SetDiffPairGapParent( m_defaultNetClass.get() );
  757. }
  758. // Currently this is only on the default netclass, and not editable in the setup panel
  759. // if( !nc->HasDiffPairViaGap() )
  760. // {
  761. // addedDefault = true;
  762. // nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
  763. // nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
  764. // }
  765. if( !nc->HasWireWidth() )
  766. {
  767. addedDefault = true;
  768. nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
  769. nc->SetWireWidthParent( m_defaultNetClass.get() );
  770. }
  771. if( !nc->HasBusWidth() )
  772. {
  773. addedDefault = true;
  774. nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
  775. nc->SetBusWidthParent( m_defaultNetClass.get() );
  776. }
  777. return addedDefault;
  778. }
  779. std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
  780. {
  781. auto ii = m_netClasses.find( aNetClassName );
  782. if( ii == m_netClasses.end() )
  783. return m_defaultNetClass;
  784. else
  785. return ii->second;
  786. }
  787. static bool isSuperSubOverbar( wxChar c )
  788. {
  789. return c == '_' || c == '^' || c == '~';
  790. }
  791. bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
  792. std::vector<wxString>* aMemberList )
  793. {
  794. auto isDigit = []( wxChar c )
  795. {
  796. static wxString digits( wxT( "0123456789" ) );
  797. return digits.Contains( c );
  798. };
  799. size_t busLen = aBus.length();
  800. size_t i = 0;
  801. wxString prefix;
  802. wxString suffix;
  803. wxString tmp;
  804. long begin = 0;
  805. long end = 0;
  806. int braceNesting = 0;
  807. prefix.reserve( busLen );
  808. // Parse prefix
  809. //
  810. for( ; i < busLen; ++i )
  811. {
  812. if( aBus[i] == '{' )
  813. {
  814. if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
  815. braceNesting++;
  816. else
  817. return false;
  818. }
  819. else if( aBus[i] == '}' )
  820. {
  821. braceNesting--;
  822. }
  823. if( aBus[i] == ' ' || aBus[i] == ']' )
  824. return false;
  825. if( aBus[i] == '[' )
  826. break;
  827. prefix += aBus[i];
  828. }
  829. // Parse start number
  830. //
  831. i++; // '[' character
  832. if( i >= busLen )
  833. return false;
  834. for( ; i < busLen; ++i )
  835. {
  836. if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
  837. {
  838. tmp.ToLong( &begin );
  839. i += 2;
  840. break;
  841. }
  842. if( !isDigit( aBus[i] ) )
  843. return false;
  844. tmp += aBus[i];
  845. }
  846. // Parse end number
  847. //
  848. tmp = wxEmptyString;
  849. if( i >= busLen )
  850. return false;
  851. for( ; i < busLen; ++i )
  852. {
  853. if( aBus[i] == ']' )
  854. {
  855. tmp.ToLong( &end );
  856. ++i;
  857. break;
  858. }
  859. if( !isDigit( aBus[i] ) )
  860. return false;
  861. tmp += aBus[i];
  862. }
  863. // Parse suffix
  864. //
  865. for( ; i < busLen; ++i )
  866. {
  867. if( aBus[i] == '}' )
  868. {
  869. braceNesting--;
  870. suffix += aBus[i];
  871. }
  872. else
  873. {
  874. return false;
  875. }
  876. }
  877. if( braceNesting != 0 )
  878. return false;
  879. if( begin == end )
  880. return false;
  881. else if( begin > end )
  882. std::swap( begin, end );
  883. if( aName )
  884. *aName = prefix;
  885. if( aMemberList )
  886. {
  887. for( long idx = begin; idx <= end; ++idx )
  888. {
  889. wxString str = prefix;
  890. str << idx;
  891. str << suffix;
  892. aMemberList->emplace_back( str );
  893. }
  894. }
  895. return true;
  896. }
  897. bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
  898. std::vector<wxString>* aMemberList )
  899. {
  900. size_t groupLen = aGroup.length();
  901. size_t i = 0;
  902. wxString prefix;
  903. wxString tmp;
  904. int braceNesting = 0;
  905. prefix.reserve( groupLen );
  906. // Parse prefix
  907. //
  908. for( ; i < groupLen; ++i )
  909. {
  910. if( aGroup[i] == '{' )
  911. {
  912. if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
  913. braceNesting++;
  914. else
  915. break;
  916. }
  917. else if( aGroup[i] == '}' )
  918. {
  919. braceNesting--;
  920. }
  921. if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
  922. return false;
  923. prefix += aGroup[i];
  924. }
  925. if( braceNesting != 0 )
  926. return false;
  927. if( aName )
  928. *aName = prefix;
  929. // Parse members
  930. //
  931. i++; // '{' character
  932. if( i >= groupLen )
  933. return false;
  934. for( ; i < groupLen; ++i )
  935. {
  936. if( aGroup[i] == '{' )
  937. {
  938. if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
  939. braceNesting++;
  940. else
  941. return false;
  942. }
  943. else if( aGroup[i] == '}' )
  944. {
  945. if( braceNesting )
  946. {
  947. braceNesting--;
  948. }
  949. else
  950. {
  951. if( aMemberList && !tmp.IsEmpty() )
  952. aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
  953. return true;
  954. }
  955. }
  956. // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
  957. if( aGroup[i] == ' ' || aGroup[i] == ',' )
  958. {
  959. if( aMemberList && !tmp.IsEmpty() )
  960. aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
  961. tmp.Clear();
  962. continue;
  963. }
  964. tmp += aGroup[i];
  965. }
  966. return false;
  967. }