KiCad PCB EDA Suite
net_settings.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) 2020 CERN
5  * Copyright (C) 2021 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 
22 #include <nlohmann/json.hpp>
23 
24 
25 #include <project/net_settings.h>
26 #include <settings/parameters.h>
29 #include <kicad_string.h>
30 #include <convert_to_biu.h>
31 
32 
33 // const int netSettingsSchemaVersion = 0;
34 const int netSettingsSchemaVersion = 1; // new overbar syntax
35 
36 
37 static OPT<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
38  OPT<int> aDefault = OPT<int>() )
39 {
40  if( aObj.contains( aKey ) && aObj[aKey].is_number() )
41  return PcbMillimeter2iu( aObj[aKey].get<double>() );
42  else
43  return aDefault;
44 };
45 
46 
47 NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
48  NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath ),
49  m_NetClasses()
50 {
51  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
52  [&]() -> nlohmann::json
53  {
54  nlohmann::json ret = nlohmann::json::array();
55 
56  NETCLASSPTR netclass = m_NetClasses.GetDefault();
58 
59  for( unsigned int idx = 0; idx <= m_NetClasses.GetCount(); idx++ )
60  {
61  if( idx > 0 )
62  {
63  netclass = nc->second;
64  ++nc;
65  }
66 
67  // Note: we're in common/, but we do happen to know which of these fields
68  // are used in which units system.
69  nlohmann::json netclassJson = {
70  { "name", netclass->GetName().ToUTF8() },
71  { "wire_width", SchIu2Mils( netclass->GetWireWidth() ) },
72  { "bus_width", SchIu2Mils( netclass->GetBusWidth() ) },
73  { "line_style", netclass->GetLineStyle() },
74  { "schematic_color", netclass->GetSchematicColor() },
75  { "pcb_color", netclass->GetPcbColor() }
76  };
77 
78 
79  if( netclass->HasClearance() )
80  netclassJson.push_back( { "clearance",
81  PcbIu2Millimeter( netclass->GetClearance() ) } );
82 
83  if( netclass->HasTrackWidth() )
84  netclassJson.push_back( { "track_width",
85  PcbIu2Millimeter( netclass->GetTrackWidth() ) } );
86 
87  if( netclass->HasViaDiameter() )
88  netclassJson.push_back( { "via_diameter",
89  PcbIu2Millimeter( netclass->GetViaDiameter() ) } );
90 
91  if( netclass->HasViaDrill() )
92  netclassJson.push_back( { "via_drill",
93  PcbIu2Millimeter( netclass->GetViaDrill() ) } );
94 
95  if( netclass->HasuViaDiameter() )
96  netclassJson.push_back( { "microvia_diameter",
97  PcbIu2Millimeter( netclass->GetuViaDiameter() ) } );
98 
99  if( netclass->HasuViaDrill() )
100  netclassJson.push_back( { "microvia_drill",
101  PcbIu2Millimeter( netclass->GetuViaDrill() ) } );
102 
103  if( netclass->HasDiffPairWidth() )
104  netclassJson.push_back( { "diff_pair_width",
105  PcbIu2Millimeter( netclass->GetDiffPairWidth() ) } );
106 
107  if( netclass->HasDiffPairGap() )
108  netclassJson.push_back( { "diff_pair_gap",
109  PcbIu2Millimeter( netclass->GetDiffPairGap() ) } );
110 
111  if( netclass->HasDiffPairViaGap() )
112  netclassJson.push_back( { "diff_pair_via_gap",
113  PcbIu2Millimeter( netclass->GetDiffPairViaGap() ) } );
114 
115  if( idx > 0 ) // No need to store members of Default netclass
116  {
117  nlohmann::json membersJson = nlohmann::json::array();
118 
119  for( const wxString& member : *netclass )
120  {
121  if( !member.empty() )
122  membersJson.push_back( member );
123  }
124 
125  netclassJson["nets"] = membersJson;
126  }
127 
128  ret.push_back( netclassJson );
129  }
130 
131  return ret;
132  },
133  [&]( const nlohmann::json& aJson )
134  {
135  if( !aJson.is_array() )
136  return;
137 
139  m_NetClassAssignments.clear();
140  NETCLASSPTR netclass;
141  NETCLASSPTR defaultClass = m_NetClasses.GetDefault();
142 
143  auto getInSchematicUnits =
144  []( const nlohmann::json& aObj, const std::string& aKey, int aDefault )
145  {
146  if( aObj.contains( aKey ) && aObj[aKey].is_number() )
147  return SchMils2iu( aObj[aKey].get<double>() );
148  else
149  return aDefault;
150  };
151 
152  for( const nlohmann::json& entry : aJson )
153  {
154  if( !entry.is_object() || !entry.contains( "name" ) )
155  continue;
156 
157  wxString name = entry["name"];
158 
159  if( name == defaultClass->GetName() )
160  netclass = defaultClass;
161  else
162  netclass = std::make_shared<NETCLASS>( name );
163 
164  if( auto value = getInPcbUnits( entry, "clearance" ) )
165  netclass->SetClearance( *value );
166 
167  if( auto value = getInPcbUnits( entry, "track_width" ) )
168  netclass->SetTrackWidth( *value );
169 
170  if( auto value = getInPcbUnits( entry, "via_diameter" ) )
171  netclass->SetViaDiameter( *value );
172 
173  if( auto value = getInPcbUnits( entry, "via_drill" ) )
174  netclass->SetViaDrill( *value );
175 
176  if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
177  netclass->SetuViaDiameter( *value );
178 
179  if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
180  netclass->SetuViaDrill( *value );
181 
182  if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
183  netclass->SetDiffPairWidth( *value );
184 
185  if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
186  netclass->SetDiffPairGap( *value );
187 
188  if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
189  netclass->SetDiffPairViaGap( *value );
190 
191  netclass->SetWireWidth( getInSchematicUnits( entry, "wire_width",
192  netclass->GetWireWidth() ) );
193  netclass->SetBusWidth( getInSchematicUnits( entry, "bus_width",
194  netclass->GetWireWidth() ) );
195 
196  if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
197  netclass->SetLineStyle( entry["line_style"].get<int>() );
198 
199  if( entry.contains( "nets" ) && entry["nets"].is_array() )
200  {
201  for( const auto& net : entry["nets"].items() )
202  netclass->Add( net.value().get<wxString>() );
203  }
204 
205  if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
206  netclass->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
207 
208  if( entry.contains( "schematic_color" )
209  && entry["schematic_color"].is_string() )
210  netclass->SetSchematicColor(
211  entry["schematic_color"].get<KIGFX::COLOR4D>() );
212 
213  if( netclass != defaultClass )
214  m_NetClasses.Add( netclass );
215 
216  for( const wxString& net : *netclass )
217  m_NetClassAssignments[ net ] = netclass->GetName();
218  }
219 
221  },
222  {} ) );
223 
224  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
225  [&]() -> nlohmann::json
226  {
227  nlohmann::json ret = {};
228 
229  for( const auto& pair : m_PcbNetColors )
230  {
231  std::string key( pair.first.ToUTF8() );
232  ret[key] = pair.second;
233  }
234 
235  return ret;
236  },
237  [&]( const nlohmann::json& aJson )
238  {
239  if( !aJson.is_object() )
240  return;
241 
242  m_PcbNetColors.clear();
243 
244  for( const auto& pair : aJson.items() )
245  {
246  wxString key( pair.key().c_str(), wxConvUTF8 );
247  m_PcbNetColors[key] = pair.value().get<KIGFX::COLOR4D>();
248  }
249  },
250  {} ) );
251 
252  registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
253 }
254 
255 
257 {
258  // Release early before destroying members
259  if( m_parent )
260  {
262  m_parent = nullptr;
263  }
264 }
265 
266 
268 {
269  if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
270  {
271  for( auto& netClass : m_internals->At( "classes" ).items() )
272  {
273  if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
274  {
275  nlohmann::json migrated = nlohmann::json::array();
276 
277  for( auto& net : netClass.value()["nets"].items() )
278  migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
279 
280  netClass.value()["nets"] = migrated;
281  }
282  }
283  }
284 
285  return true;
286 }
287 
288 
289 const wxString& NET_SETTINGS::GetNetclassName( const wxString& aNetName ) const
290 {
291  static wxString defaultNetname = NETCLASS::Default;
292 
293  auto it = m_NetClassAssignments.find( aNetName );
294 
295  if( it == m_NetClassAssignments.end() )
296  return defaultNetname;
297  else
298  return it->second;
299 }
300 
301 
302 static bool isSuperSubOverbar( wxChar c )
303 {
304  return c == '_' || c == '^' || c == '~';
305 }
306 
307 
308 bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
309  std::vector<wxString>* aMemberList )
310 {
311  auto isDigit = []( wxChar c )
312  {
313  static wxString digits( wxT( "0123456789" ) );
314  return digits.Contains( c );
315  };
316 
317  size_t busLen = aBus.length();
318  size_t i = 0;
319  wxString prefix;
320  wxString suffix;
321  wxString tmp;
322  long begin = 0;
323  long end = 0;
324  int braceNesting = 0;
325 
326  prefix.reserve( busLen );
327 
328  // Parse prefix
329  //
330  for( ; i < busLen; ++i )
331  {
332  if( aBus[i] == '{' )
333  {
334  if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
335  braceNesting++;
336  else
337  return false;
338  }
339  else if( aBus[i] == '}' )
340  {
341  braceNesting--;
342  }
343 
344  if( aBus[i] == ' ' || aBus[i] == ']' )
345  return false;
346 
347  if( aBus[i] == '[' )
348  break;
349 
350  prefix += aBus[i];
351  }
352 
353  // Parse start number
354  //
355  i++; // '[' character
356 
357  if( i >= busLen )
358  return false;
359 
360  for( ; i < busLen; ++i )
361  {
362  if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
363  {
364  tmp.ToLong( &begin );
365  i += 2;
366  break;
367  }
368 
369  if( !isDigit( aBus[i] ) )
370  return false;
371 
372  tmp += aBus[i];
373  }
374 
375  // Parse end number
376  //
377  tmp = wxEmptyString;
378 
379  if( i >= busLen )
380  return false;
381 
382  for( ; i < busLen; ++i )
383  {
384  if( aBus[i] == ']' )
385  {
386  tmp.ToLong( &end );
387  ++i;
388  break;
389  }
390 
391  if( !isDigit( aBus[i] ) )
392  return false;
393 
394  tmp += aBus[i];
395  }
396 
397  // Parse suffix
398  //
399  for( ; i < busLen; ++i )
400  {
401  if( aBus[i] == '}' )
402  {
403  braceNesting--;
404  suffix += aBus[i];
405  }
406  else
407  {
408  return false;
409  }
410  }
411 
412  if( braceNesting != 0 )
413  return false;
414 
415  if( begin == end )
416  return false;
417  else if( begin > end )
418  std::swap( begin, end );
419 
420  if( aName )
421  *aName = prefix;
422 
423  if( aMemberList )
424  {
425  for( long idx = begin; idx <= end; ++idx )
426  {
427  wxString str = prefix;
428  str << idx;
429  str << suffix;
430 
431  aMemberList->emplace_back( str );
432  }
433  }
434 
435  return true;
436 }
437 
438 
439 bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
440  std::vector<wxString>* aMemberList )
441 {
442  size_t groupLen = aGroup.length();
443  size_t i = 0;
444  wxString prefix;
445  wxString suffix;
446  wxString tmp;
447  int braceNesting = 0;
448 
449  prefix.reserve( groupLen );
450 
451  // Parse prefix
452  //
453  for( ; i < groupLen; ++i )
454  {
455  if( aGroup[i] == '{' )
456  {
457  if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
458  braceNesting++;
459  else
460  break;
461  }
462  else if( aGroup[i] == '}' )
463  {
464  braceNesting--;
465  }
466 
467  if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
468  return false;
469 
470  prefix += aGroup[i];
471  }
472 
473  if( braceNesting != 0 )
474  return false;
475 
476  if( aName )
477  *aName = prefix;
478 
479  // Parse members
480  //
481  i++; // '{' character
482 
483  if( i >= groupLen )
484  return false;
485 
486  for( ; i < groupLen; ++i )
487  {
488  if( aGroup[i] == '{' )
489  {
490  if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
491  braceNesting++;
492  else
493  return false;
494  }
495  else if( aGroup[i] == '}' )
496  {
497  if( braceNesting )
498  {
499  braceNesting--;
500  }
501  else
502  {
503  if( aMemberList && !tmp.IsEmpty() )
504  aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
505 
506  return true;
507  }
508  }
509 
510  // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
511  if( aGroup[i] == ' ' || aGroup[i] == ',' )
512  {
513  if( aMemberList && !tmp.IsEmpty() )
514  aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
515 
516  tmp.Clear();
517  continue;
518  }
519 
520  tmp += aGroup[i];
521  }
522 
523  return false;
524 }
525 
526 
527 void NET_SETTINGS::ResolveNetClassAssignments( bool aRebuildFromScratch )
528 {
529  std::map<wxString, wxString> baseList;
530 
531  if( aRebuildFromScratch )
532  {
533  for( const std::pair<const wxString, NETCLASSPTR>& netclass : m_NetClasses )
534  {
535  for( const wxString& net : *netclass.second )
536  baseList[ net ] = netclass.second->GetName();
537  }
538  }
539  else
540  {
541  baseList = m_NetClassAssignments;
542  }
543 
544  m_NetClassAssignments.clear();
545 
546  for( const auto& ii : baseList )
547  {
548  m_NetClassAssignments[ ii.first ] = ii.second;
549 
550  wxString unescaped = UnescapeString( ii.first );
551  wxString prefix;
552  std::vector<wxString> members;
553 
554  if( ParseBusVector( unescaped, &prefix, &members ) )
555  {
556  prefix = wxEmptyString;
557  }
558  else if( ParseBusGroup( unescaped, &prefix, &members ) )
559  {
560  if( !prefix.IsEmpty() )
561  prefix += wxT( "." );
562  }
563 
564  for( wxString& member : members )
565  m_NetClassAssignments[ prefix + member ] = ii.second;
566  }
567 }
std::map< wxString, wxString > m_NetClassAssignments
Definition: net_settings.h:44
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
const wxString & GetNetclassName(const wxString &aNetName) const
std::map< wxString, KIGFX::COLOR4D > m_PcbNetColors
A map of fully-qualified net names to colors used in the board context.
Definition: net_settings.h:52
virtual ~NET_SETTINGS()
nlohmann::json json
Definition: gerbview.cpp:41
NETCLASS_MAP::const_iterator const_iterator
Definition: netclass.h:238
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parse a bus group label into the name and a list of components.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
constexpr int PcbMillimeter2iu(double mm)
iterator begin()
Definition: netclass.h:235
NETCLASSES m_NetClasses
Definition: net_settings.h:40
bool isDigit(char cc)
Definition: dsnlexer.cpp:435
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:49
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
void ResolveNetClassAssignments(bool aRebuildFromScratch=false)
Explode the list of netclass assignments to include atomic members of composite labels (buses).
JSON_SETTINGS * m_parent
A pointer to the parent object to load and store from.
constexpr double SchIu2Mils(int iu)
bool Add(const NETCLASSPTR &aNetclass)
Add aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
void Clear()
Destroy any contained NETCLASS instances except the default one, and clears any members from the defa...
Definition: netclass.h:228
static bool isSuperSubOverbar(wxChar c)
const char * name
Definition: DXF_plotter.cpp:59
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:214
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
Definition: string.cpp:141
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
unsigned GetCount() const
Definition: netclass.h:245
boost::optional< T > OPT
Definition: optional.h:7
NETCLASSPTR GetDefault() const
Definition: netclass.h:253
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
Definition: string.cpp:44
static OPT< int > getInPcbUnits(const nlohmann::json &aObj, const std::string &aKey, OPT< int > aDefault=OPT< int >())
constexpr int SchMils2iu(int mils)
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
bool migrateSchema0to1()
const int netSettingsSchemaVersion
NET_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103