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