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  * @author Jon Evans <jon@craftyjon.com>
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <project/net_settings.h>
22 #include <settings/parameters.h>
24 #include <kicad_string.h>
25 #include <convert_to_biu.h>
26 
28 
29 static OPT<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey, OPT<int> aDefault = OPT<int>() )
30 {
31  if( aObj.contains( aKey ) && aObj[aKey].is_number() )
32  return PcbMillimeter2iu( aObj[aKey].get<double>() );
33  else
34  return aDefault;
35 };
36 
37 NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
38  NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath ),
39  m_NetClasses()
40 {
41  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
42  [&]() -> nlohmann::json
43  {
44  nlohmann::json ret = nlohmann::json::array();
45 
46  NETCLASSPTR netclass = m_NetClasses.GetDefault();
48 
49  for( unsigned int idx = 0; idx <= m_NetClasses.GetCount(); idx++ )
50  {
51  if( idx > 0 )
52  {
53  netclass = nc->second;
54  ++nc;
55  }
56 
57  // Note: we're in common/, but we do happen to know which of these fields
58  // are used in which units system.
59  nlohmann::json netclassJson = {
60  { "name", netclass->GetName().ToUTF8() },
61  { "wire_width", SchIu2Mils( netclass->GetWireWidth() ) },
62  { "bus_width", SchIu2Mils( netclass->GetBusWidth() ) },
63  { "line_style", netclass->GetLineStyle() },
64  { "schematic_color", netclass->GetSchematicColor() },
65  { "pcb_color", netclass->GetPcbColor() }
66  };
67 
68 
69  if( netclass->HasClearance() )
70  netclassJson.push_back( { "clearance", PcbIu2Millimeter( netclass->GetClearance() ) } );
71 
72  if( netclass->HasTrackWidth() )
73  netclassJson.push_back( { "track_width", PcbIu2Millimeter( netclass->GetTrackWidth() ) } );
74 
75  if( netclass->HasViaDiameter() )
76  netclassJson.push_back( { "via_diameter", PcbIu2Millimeter( netclass->GetViaDiameter() ) } );
77 
78  if( netclass->HasViaDrill() )
79  netclassJson.push_back( { "via_drill", PcbIu2Millimeter( netclass->GetViaDrill() ) } );
80 
81  if( netclass->HasuViaDiameter() )
82  netclassJson.push_back( { "microvia_diameter", PcbIu2Millimeter( netclass->GetuViaDiameter() ) } );
83 
84  if( netclass->HasuViaDrill() )
85  netclassJson.push_back( { "microvia_drill", PcbIu2Millimeter( netclass->GetuViaDrill() ) } );
86 
87  if( netclass->HasDiffPairWidth() )
88  netclassJson.push_back( { "diff_pair_width", PcbIu2Millimeter( netclass->GetDiffPairWidth() ) } );
89 
90  if( netclass->HasDiffPairGap() )
91  netclassJson.push_back( { "diff_pair_gap", PcbIu2Millimeter( netclass->GetDiffPairGap() ) } );
92 
93  if( netclass->HasDiffPairViaGap() )
94  netclassJson.push_back( { "diff_pair_via_gap", PcbIu2Millimeter( netclass->GetDiffPairViaGap() ) } );
95 
96  if( idx > 0 ) // No need to store members of Default netclass
97  {
98  nlohmann::json membersJson = nlohmann::json::array();
99 
100  for( const wxString& member : *netclass )
101  {
102  if( !member.empty() )
103  membersJson.push_back( member );
104  }
105 
106  netclassJson["nets"] = membersJson;
107  }
108 
109  ret.push_back( netclassJson );
110  }
111 
112  return ret;
113  },
114  [&]( const nlohmann::json& aJson )
115  {
116  if( !aJson.is_array() )
117  return;
118 
120  m_NetClassAssignments.clear();
121  NETCLASSPTR netclass;
122  NETCLASSPTR defaultClass = m_NetClasses.GetDefault();
123 
124  auto getInSchematicUnits =
125  []( const nlohmann::json& aObj, const std::string& aKey, int aDefault )
126  {
127  if( aObj.contains( aKey ) && aObj[aKey].is_number() )
128  return SchMils2iu( aObj[aKey].get<double>() );
129  else
130  return aDefault;
131  };
132 
133  for( const nlohmann::json& entry : aJson )
134  {
135  if( !entry.is_object() || !entry.contains( "name" ) )
136  continue;
137 
138  wxString name = entry["name"];
139 
140  if( name == defaultClass->GetName() )
141  netclass = defaultClass;
142  else
143  netclass = std::make_shared<NETCLASS>( name );
144 
145  if( auto value = getInPcbUnits( entry, "clearance" ) )
146  netclass->SetClearance( *value );
147 
148  if( auto value = getInPcbUnits( entry, "track_width" ) )
149  netclass->SetTrackWidth( *value );
150 
151  if( auto value = getInPcbUnits( entry, "via_diameter" ) )
152  netclass->SetViaDiameter( *value );
153 
154  if( auto value = getInPcbUnits( entry, "via_drill" ) )
155  netclass->SetViaDrill( *value );
156 
157  if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
158  netclass->SetuViaDiameter( *value );
159 
160  if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
161  netclass->SetuViaDrill( *value );
162 
163  if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
164  netclass->SetDiffPairWidth( *value );
165 
166  if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
167  netclass->SetDiffPairGap( *value );
168 
169  if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
170  netclass->SetDiffPairViaGap( *value );
171 
172  netclass->SetWireWidth( getInSchematicUnits( entry, "wire_width",
173  netclass->GetWireWidth() ) );
174  netclass->SetBusWidth( getInSchematicUnits( entry, "bus_width",
175  netclass->GetWireWidth() ) );
176 
177  if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
178  netclass->SetLineStyle( entry["line_style"].get<int>() );
179 
180  if( entry.contains( "nets" ) && entry["nets"].is_array() )
181  {
182  for( const auto& net : entry["nets"].items() )
183  netclass->Add( net.value().get<wxString>() );
184  }
185 
186  if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
187  netclass->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
188 
189  if( entry.contains( "schematic_color" ) && entry["schematic_color"].is_string() )
190  netclass->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
191 
192  if( netclass != defaultClass )
193  m_NetClasses.Add( netclass );
194 
195  for( const wxString& net : *netclass )
196  m_NetClassAssignments[ net ] = netclass->GetName();
197  }
198 
200  },
201  {} ) );
202 
203  m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
204  [&]() -> nlohmann::json
205  {
206  nlohmann::json ret = {};
207 
208  for( const auto& pair : m_PcbNetColors )
209  {
210  std::string key( pair.first.ToUTF8() );
211  ret[key] = pair.second;
212  }
213 
214  return ret;
215  },
216  [&]( const nlohmann::json& aJson )
217  {
218  if( !aJson.is_object() )
219  return;
220 
221  m_PcbNetColors.clear();
222 
223  for( const auto& pair : aJson.items() )
224  {
225  wxString key( pair.key().c_str(), wxConvUTF8 );
226  m_PcbNetColors[key] = pair.value().get<KIGFX::COLOR4D>();
227  }
228  },
229  {} ) );
230 }
231 
232 
234 {
235  // Release early before destroying members
236  if( m_parent )
237  {
239  m_parent = nullptr;
240  }
241 }
242 
243 
244 const wxString& NET_SETTINGS::GetNetclassName( const wxString& aNetName ) const
245 {
246  static wxString defaultNetname = NETCLASS::Default;
247 
248  auto it = m_NetClassAssignments.find( aNetName );
249 
250  if( it == m_NetClassAssignments.end() )
251  return defaultNetname;
252  else
253  return it->second;
254 }
255 
256 
257 static bool isSuperSub( wxChar c )
258 {
259  return c == '_' || c == '^';
260 }
261 
262 
263 bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
264  std::vector<wxString>* aMemberList )
265 {
266  auto isDigit = []( wxChar c )
267  {
268  static wxString digits( wxT( "0123456789" ) );
269  return digits.Contains( c );
270  };
271 
272  size_t busLen = aBus.length();
273  size_t i = 0;
274  wxString prefix;
275  wxString suffix;
276  wxString tmp;
277  long begin = 0;
278  long end = 0;
279  int braceNesting = 0;
280 
281  prefix.reserve( busLen );
282 
283  // Parse prefix
284  //
285  for( ; i < busLen; ++i )
286  {
287  if( aBus[i] == '{' )
288  {
289  if( i > 0 && isSuperSub( aBus[i-1] ) )
290  braceNesting++;
291  else
292  return false;
293  }
294  else if( aBus[i] == '}' )
295  {
296  braceNesting--;
297  }
298 
299  if( aBus[i] == ' ' || aBus[i] == ']' )
300  return false;
301 
302  if( aBus[i] == '[' )
303  break;
304 
305  prefix += aBus[i];
306  }
307 
308  // Parse start number
309  //
310  i++; // '[' character
311 
312  if( i >= busLen )
313  return false;
314 
315  for( ; i < busLen; ++i )
316  {
317  if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
318  {
319  tmp.ToLong( &begin );
320  i += 2;
321  break;
322  }
323 
324  if( !isDigit( aBus[i] ) )
325  return false;
326 
327  tmp += aBus[i];
328  }
329 
330  // Parse end number
331  //
332  tmp = wxEmptyString;
333 
334  if( i >= busLen )
335  return false;
336 
337  for( ; i < busLen; ++i )
338  {
339  if( aBus[i] == ']' )
340  {
341  tmp.ToLong( &end );
342  ++i;
343  break;
344  }
345 
346  if( !isDigit( aBus[i] ) )
347  return false;
348 
349  tmp += aBus[i];
350  }
351 
352  // Parse suffix
353  //
354  for( ; i < busLen; ++i )
355  {
356  if( aBus[i] == '}' )
357  {
358  braceNesting--;
359  suffix += aBus[i];
360  }
361  else if( aBus[i] == '~' )
362  {
363  suffix += aBus[i];
364  }
365  else
366  {
367  return false;
368  }
369  }
370 
371  if( braceNesting != 0 )
372  return false;
373 
374  if( begin == end )
375  return false;
376  else if( begin > end )
377  std::swap( begin, end );
378 
379  if( aName )
380  *aName = prefix;
381 
382  if( aMemberList )
383  {
384  for( long idx = begin; idx <= end; ++idx )
385  {
386  wxString str = prefix;
387  str << idx;
388  str << suffix;
389 
390  aMemberList->emplace_back( str );
391  }
392  }
393 
394  return true;
395 }
396 
397 
398 bool NET_SETTINGS::ParseBusGroup( wxString aGroup, wxString* aName,
399  std::vector<wxString>* aMemberList )
400 {
401  size_t groupLen = aGroup.length();
402  size_t i = 0;
403  wxString prefix;
404  wxString suffix;
405  wxString tmp;
406  int braceNesting = 0;
407 
408  prefix.reserve( groupLen );
409 
410  // Parse prefix
411  //
412  for( ; i < groupLen; ++i )
413  {
414  if( aGroup[i] == '{' )
415  {
416  if( i > 0 && isSuperSub( aGroup[i-1] ) )
417  braceNesting++;
418  else
419  break;
420  }
421  else if( aGroup[i] == '}' )
422  {
423  braceNesting--;
424  }
425 
426  if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
427  return false;
428 
429  prefix += aGroup[i];
430  }
431 
432  if( braceNesting != 0 )
433  return false;
434 
435  if( aName )
436  *aName = prefix;
437 
438  // Parse members
439  //
440  i++; // '{' character
441 
442  if( i >= groupLen )
443  return false;
444 
445  for( ; i < groupLen; ++i )
446  {
447  if( aGroup[i] == '{' )
448  {
449  if( i > 0 && isSuperSub( aGroup[i-1] ) )
450  braceNesting++;
451  else
452  return false;
453  }
454  else if( aGroup[i] == '}' )
455  {
456  if( braceNesting )
457  braceNesting--;
458  else
459  {
460  if( aMemberList )
461  aMemberList->push_back( tmp );
462 
463  return true;
464  }
465  }
466 
467  if( aGroup[i] == ' ' )
468  {
469  if( aMemberList )
470  aMemberList->push_back( tmp );
471 
472  tmp.Clear();
473  continue;
474  }
475 
476  tmp += aGroup[i];
477  }
478 
479  return false;
480 }
481 
482 
483 void NET_SETTINGS::ResolveNetClassAssignments( bool aRebuildFromScratch )
484 {
485  std::map<wxString, wxString> baseList;
486 
487  if( aRebuildFromScratch )
488  {
489  for( const std::pair<const wxString, NETCLASSPTR>& netclass : m_NetClasses )
490  {
491  for( const wxString& net : *netclass.second )
492  baseList[ net ] = netclass.second->GetName();
493  }
494  }
495  else
496  {
497  baseList = m_NetClassAssignments;
498  }
499 
500  m_NetClassAssignments.clear();
501 
502  for( const auto& ii : baseList )
503  {
504  m_NetClassAssignments[ ii.first ] = ii.second;
505 
506  wxString unescaped = UnescapeString( ii.first );
507  wxString prefix;
508  std::vector<wxString> members;
509 
510  if( ParseBusVector( unescaped, &prefix, &members ) )
511  {
512  prefix = wxEmptyString;
513  }
514  else if( ParseBusGroup( unescaped, &prefix, &members ) )
515  {
516  if( !prefix.IsEmpty() )
517  prefix += wxT( "." );
518  }
519 
520  for( wxString& member : members )
521  m_NetClassAssignments[ prefix + member ] = ii.second;
522  }
523 }
std::map< wxString, wxString > m_NetClassAssignments
Definition: net_settings.h:43
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:51
virtual ~NET_SETTINGS()
nlohmann::json json
Definition: gerbview.cpp:39
NETCLASS_MAP::const_iterator const_iterator
Definition: netclass.h:249
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
constexpr int PcbMillimeter2iu(double mm)
static bool ParseBusGroup(wxString aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parses a bus group label into the name and a list of components.
iterator begin()
Definition: netclass.h:246
NETCLASSES m_NetClasses
Definition: net_settings.h:39
bool isDigit(char cc)
Definition: dsnlexer.cpp:434
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:80
void ResolveNetClassAssignments(bool aRebuildFromScratch=false)
Explodes 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)
Function Add takes aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
void Clear()
Function Clear destroys any contained NETCLASS instances except the Default one, and clears any membe...
Definition: netclass.h:239
const char * name
Definition: DXF_plotter.cpp:59
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:152
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parses a bus vector (e.g.
unsigned GetCount() const
Function GetCount.
Definition: netclass.h:257
boost::optional< T > OPT
Definition: optional.h:7
NETCLASSPTR GetDefault() const
Function GetDefault.
Definition: netclass.h:266
static bool isSuperSub(wxChar c)
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.
const int netSettingsSchemaVersion
NET_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:100