KiCad PCB EDA Suite
sch_connection.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) 2018 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Jon Evans <[email protected]>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <regex>
24 #include <wx/tokenzr.h>
25 
26 #include <connection_graph.h>
27 #include <sch_symbol.h>
28 #include <sch_pin.h>
29 #include <sch_screen.h>
30 #include <project/net_settings.h>
31 #include <advanced_config.h>
32 #include <string_utils.h>
33 
34 #include <sch_connection.h>
35 
65  m_sheet( aPath ),
66  m_local_sheet( aPath ),
67  m_parent( aParent ),
68  m_driver( nullptr ),
69  m_graph( nullptr )
70 {
71  Reset();
72 }
73 
74 
76  m_sheet( SCH_SHEET_PATH() ),
77  m_local_sheet( SCH_SHEET_PATH() ),
78  m_parent( nullptr ),
79  m_driver( nullptr ),
80  m_graph( aGraph )
81 {
82  Reset();
83 }
84 
85 
86 bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
87 {
88  // NOTE: Not comparing m_dirty or net/bus/subgraph codes
89  if( ( aOther.m_driver == m_driver ) &&
90  ( aOther.m_type == m_type ) &&
91  ( aOther.m_name == m_name ) &&
92  ( aOther.m_sheet == m_sheet ) )
93  {
94  return true;
95  }
96 
97  return false;
98 }
99 
100 
102 {
103  m_driver = aItem;
104 
105  recacheName();
106 
107  for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
108  member->SetDriver( aItem );
109 }
110 
111 
113 {
114  m_sheet = aSheet;
115  m_local_sheet = aSheet;
116 
117  recacheName();
118 
119  for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
120  member->SetSheet( aSheet );
121 }
122 
123 
124 bool SCH_CONNECTION::operator!=( const SCH_CONNECTION& aOther ) const
125 {
126  return !( aOther == *this );
127 }
128 
129 
130 void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
131 {
132  m_members.clear();
133 
134  m_name = aLabel;
135  m_local_name = aLabel;
137 
138  wxString prefix;
139  std::vector<wxString> members;
140 
141  wxString unescaped = UnescapeString( aLabel );
142 
143  if( NET_SETTINGS::ParseBusVector( unescaped, &prefix, &members ) )
144  {
146  m_vector_prefix = prefix;
147 
148  long i = 0;
149 
150  for( const wxString& vector_member : members )
151  {
152  auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
153  member->m_type = CONNECTION_TYPE::NET;
154  member->m_prefix = m_prefix;
155  member->m_local_name = vector_member;
156  member->m_local_prefix = m_prefix;
157  member->m_vector_index = i++;
158  member->SetName( vector_member );
159  member->SetGraph( m_graph );
160  m_members.push_back( member );
161  }
162  }
163  else if( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
164  {
166  m_bus_prefix = prefix;
167 
168  // Named bus groups generate a net prefix, unnamed ones don't
169  if( !prefix.IsEmpty() )
170  prefix += wxT( "." );
171 
172  for( const wxString& group_member : members )
173  {
174  // Handle alias inside bus group member list
175  if( auto alias = m_graph->GetBusAlias( group_member ) )
176  {
177  for( const wxString& alias_member : alias->Members() )
178  {
179  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
180  member->SetPrefix( prefix );
181  member->SetGraph( m_graph );
182  member->ConfigureFromLabel( alias_member );
183  m_members.push_back( member );
184  }
185  }
186  else
187  {
188  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
189  member->SetPrefix( prefix );
190  member->SetGraph( m_graph );
191  member->ConfigureFromLabel( group_member );
192  m_members.push_back( member );
193  }
194  }
195  }
196  else
197  {
199  }
200 
201  recacheName();
202 }
203 
204 
206 {
208  m_name.Empty();
209  m_local_name.Empty();
210  m_local_prefix.Empty();
211  m_cached_name.Empty();
212  m_cached_name_with_path.Empty();
213  m_prefix.Empty();
214  m_bus_prefix.Empty();
215  m_suffix .Empty();
217  m_driver = nullptr;
218  m_members.clear();
219  m_dirty = true;
220  m_net_code = 0;
221  m_bus_code = 0;
222  m_subgraph_code = 0;
223  m_vector_start = 0;
224  m_vector_end = 0;
225  m_vector_index = 0;
226  m_vector_prefix.Empty();
227 }
228 
229 
231 {
232  m_graph = aOther.m_graph;
233  // Note: m_lastDriver is not cloned as it needs to be the last driver of *this* connection
234  m_driver = aOther.Driver();
235  m_sheet = aOther.Sheet();
236  // Note: m_local_sheet is not cloned
237  m_name = aOther.m_name;
238  // Note: m_local_name is not cloned if not set yet
239  if( m_local_name.IsEmpty() )
240  {
241  m_local_name = aOther.LocalName();
242  m_local_prefix = aOther.Prefix();
243  }
244 
245  m_prefix = aOther.Prefix();
246  // m_bus_prefix is not cloned; only used for local names
247  m_suffix = aOther.Suffix();
248  m_net_code = aOther.NetCode();
249  m_bus_code = aOther.BusCode();
250  m_vector_start = aOther.VectorStart();
251  m_vector_end = aOther.VectorEnd();
252  // Note: m_vector_index is not cloned
253  m_vector_prefix = aOther.VectorPrefix();
254 
255  // Note: subgraph code isn't cloned, it should remain with the original object
256 
257  // Handle vector bus members: make sure local names are preserved where possible
258  const std::vector<std::shared_ptr<SCH_CONNECTION>>& otherMembers = aOther.Members();
259 
260  if( m_type == CONNECTION_TYPE::BUS && aOther.Type() == CONNECTION_TYPE::BUS )
261  {
262  if( m_members.empty() )
263  {
264  m_members = otherMembers;
265  }
266  else
267  {
268  size_t cloneLimit = std::min( m_members.size(), otherMembers.size() );
269 
270  for( size_t i = 0; i < cloneLimit; ++i )
271  m_members[i]->Clone( *otherMembers[i] );
272  }
273  }
275  {
276  if( m_members.empty() )
277  {
278  m_members = otherMembers;
279  }
280  else
281  {
282  // TODO: refactor this once we support deep nesting
283  for( size_t i = 0; i < m_members.size(); ++i )
284  {
285  auto it = std::find_if( otherMembers.begin(), otherMembers.end(),
286  [&]( const std::shared_ptr<SCH_CONNECTION>& aTest )
287  {
288  return aTest->LocalName() == m_members[i]->LocalName();
289  } );
290 
291  if( it != otherMembers.end() )
292  m_members[i]->Clone( **it );
293  }
294  }
295  }
296 
297  m_type = aOther.Type();
298 
299  recacheName();
300 }
301 
302 
304 {
305  wxASSERT( Parent() );
306 
307  switch( Parent()->Type() )
308  {
309  case SCH_LABEL_T:
310  case SCH_GLOBAL_LABEL_T:
311  case SCH_HIER_LABEL_T:
312  case SCH_SHEET_PIN_T:
313  case SCH_SHEET_T:
314  case LIB_PIN_T:
315  return true;
316 
317  case SCH_PIN_T:
318  {
319  auto pin = static_cast<SCH_PIN*>( Parent() );
320 
321  // Only annotated symbols should drive nets.
322  return pin->IsPowerConnection() || pin->GetParentSymbol()->IsAnnotated( &m_sheet );
323  }
324 
325  default:
326  return false;
327  }
328 }
329 
330 
332 {
333  return m_driver != m_lastDriver;
334 }
335 
336 
338 {
340 }
341 
342 
343 
344 wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
345 {
346  wxASSERT( !m_cached_name.IsEmpty() );
347  return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
348 }
349 
350 
352 {
353  m_cached_name = m_name.IsEmpty() ? wxT( "<NO NET>" ) : m_prefix + m_name + m_suffix;
354 
355  bool prepend_path = true;
356 
357  if( !Parent() || m_type == CONNECTION_TYPE::NONE )
358  prepend_path = false;
359 
360  if( m_driver )
361  {
362  switch( m_driver->Type() )
363  {
364  case SCH_GLOBAL_LABEL_T:
365  case SCH_PIN_T:
366  // Pins are either power connections or belong to a uniquely-annotated
367  // symbol, so they don't need a path if they are driving the subgraph.
368  prepend_path = false;
369  break;
370 
371  default:
372  break;
373  }
374  }
375 
378 }
379 
380 
381 void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
382 {
383  m_prefix = aPrefix;
384 
385  recacheName();
386 
387  for( const auto& m : Members() )
388  m->SetPrefix( aPrefix );
389 }
390 
391 
392 void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
393 {
394  m_suffix = aSuffix;
395 
396  recacheName();
397 
398  for( const auto& m : Members() )
399  m->SetSuffix( aSuffix );
400 }
401 
402 
403 void SCH_CONNECTION::AppendInfoToMsgPanel( std::vector<MSG_PANEL_ITEM>& aList ) const
404 {
405  wxString msg, group_name, members;
406  std::vector<wxString> group_members;
407 
408  aList.emplace_back( _( "Connection Name" ), UnescapeString( Name() ) );
409 
410  // NOTE(JE) Disabling this for now, because net codes are generated in the netlist exporter
411  // in order to avoid sort costs. It may make sense to just tear out net codes from the
412  // CONNECTION_GRAPH entirely in the future, as they are mostly only useful for netlist exports.
413 #if 0
414  if( !IsBus() )
415  aList.emplace_back( _( "Net Code" ), wxString::Format( "%d", m_net_code ) );
416 #endif
417 
418  if( auto alias = m_graph->GetBusAlias( m_name ) )
419  {
420  msg.Printf( _( "Bus Alias %s Members" ), m_name );
421 
422  for( const wxString& member : alias->Members() )
423  members << member << wxT( " " );
424 
425  aList.emplace_back( msg, members );
426  }
427  else if( NET_SETTINGS::ParseBusGroup( m_name, &group_name, &group_members ) )
428  {
429  for( const wxString& group_member : group_members )
430  {
431  if( std::shared_ptr<BUS_ALIAS> group_alias = m_graph->GetBusAlias( group_member ) )
432  {
433  msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
434 
435  for( const wxString& member : group_alias->Members() )
436  members << member << wxT( " " );
437 
438  aList.emplace_back( msg, members );
439  }
440  }
441  }
442 
443 #if defined(DEBUG)
444  // These messages are not flagged as translatable, because they are only debug messages
445 
446  if( !ADVANCED_CFG::GetCfg().m_RealTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
447  return;
448 
449  if( IsBus() )
450  aList.emplace_back( wxT( "Bus Code" ), wxString::Format( wxT( "%d" ), m_bus_code ) );
451 
452  aList.emplace_back( wxT( "Subgraph Code" ), wxString::Format( wxT( "%d" ), m_subgraph_code ) );
453 
454  if( SCH_ITEM* driver = Driver() )
455  {
456  msg.Printf( wxT( "%s at %p" ), driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ), driver );
457  aList.emplace_back( wxT( "Connection Source" ), msg );
458  }
459 #endif
460 }
461 
462 
463 bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
464 {
465  const wxString& unescaped = UnescapeString( aLabel );
466 
467  return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
468  || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
469 }
470 
471 
472 bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
473 {
474  // Weak heuristic for performance reasons. Stronger test will be used for connectivity
475  wxString label = UnescapeString( aLabel );
476 
477  return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
478 }
479 
480 
481 const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
482 {
483  std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
484 
485  for( const auto& member : m_members )
486  if( member->IsBus() )
487  ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
488 
489  return ret;
490 }
491 
492 
493 static bool isSuperSubOverbar( wxChar c )
494 {
495  return c == '_' || c == '^' || c == '~';
496 };
497 
498 
499 wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
500 {
501  size_t groupLen = aGroup.length();
502  size_t i = 0;
503  wxString ret;
504  int braceNesting = 0;
505 
506  // Parse prefix
507  //
508  for( ; i < groupLen; ++i )
509  {
510  if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
511  {
512  braceNesting++;
513  i++;
514  continue;
515  }
516  else if( aGroup[i] == '}' )
517  {
518  braceNesting--;
519  continue;
520  }
521 
522  ret += aGroup[i];
523 
524  if( aGroup[i] == '{' )
525  break;
526  }
527 
528  // Parse members
529  //
530  i++; // '{' character
531 
532  for( ; i < groupLen; ++i )
533  {
534  if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
535  {
536  braceNesting++;
537  i++;
538  continue;
539  }
540  else if( aGroup[i] == '}' )
541  {
542  braceNesting--;
543  continue;
544  }
545 
546  ret += aGroup[i];
547 
548  if( aGroup[i] == '}' )
549  break;
550  }
551 
552  return ret;
553 }
554 
555 
557 {
558  if( !aOther->IsBus() )
559  return false;
560 
561  for( const auto& member : aOther->Members() )
562  {
563  if( member->FullLocalName() == FullLocalName() )
564  return true;
565  }
566 
567  return false;
568 }
569 
570 
572 {
573  if( !aOther->IsBus() )
574  return false;
575 
576  auto me = Name( true );
577 
578  for( const auto& m : aOther->Members() )
579  if( m->Name( true ) == me )
580  return true;
581 
582  return false;
583 }
static wxString PrintBusForUI(const wxString &aString)
int m_subgraph_code
Groups directly-connected items.
wxString Prefix() const
static bool MightBeBusLabel(const wxString &aLabel)
Test if aLabel looks like a bus notation.
void Clone(const SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
wxString m_cached_name
Full name, including prefix and suffix.
std::vector< std::shared_ptr< SCH_CONNECTION > > m_members
For bus connections, store a list of member connections.
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Returns a bus alias pointer for the given name if it exists (from cache)
Calculates the connectivity of a schematic and generates netlists.
wxString m_name
Name of the connection.
SCH_ITEM * m_driver
The SCH_ITEM that drives this connection's net.
static bool IsBusLabel(const wxString &aLabel)
Test if aLabel has a bus notation.
This item represents a bus group.
wxString m_bus_prefix
Optional prefix of a bux group (always empty for nets and vector buses)
wxString PathHumanReadable(bool aUseShortRootName=true) const
Return the sheet path in a human readable form made from the sheet names.
SCH_CONNECTION(SCH_ITEM *aParent=nullptr, SCH_SHEET_PATH aPath=SCH_SHEET_PATH())
Buses can be defined in multiple ways.
bool IsMemberOfBus(SCH_CONNECTION *aOther) const
Returns true if this connection is a member of bus connection aOther.
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
void SetDriver(SCH_ITEM *aItem)
void SetSheet(SCH_SHEET_PATH aSheet)
wxString m_cached_name_with_path
Full name including sheet path (if not global)
wxString LocalName() const
bool HasDriverChanged() const
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.
int BusCode() const
CONNECTION_TYPE m_type
No connection to this item.
int NetCode() const
wxString Suffix() const
wxString Name(bool aIgnoreSheet=false) const
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
void AppendInfoToMsgPanel(std::vector< MSG_PANEL_ITEM > &aList) const
Adds information about the connection object to aList.
long VectorEnd() const
void Reset()
Clears connectivity information.
wxString m_local_prefix
Local prefix for group bus members (used with m_local_name)
SCH_ITEM * Driver() const
wxString m_prefix
Prefix if connection is member of a labeled bus group (or "" if not)
#define _(s)
CONNECTION_GRAPH * m_graph
Pointer to the connection graph for the schematic this connection exists on.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
wxString FullLocalName() const
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
SCH_SHEET_PATH m_sheet
The hierarchical sheet this connection is on.
wxString UnescapeString(const wxString &aSource)
This item represents a net.
wxString m_suffix
Name suffix (used only for disambiguation)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static bool m_allowRealTime
wxString m_local_name
For bus members, we want to keep track of the "local" name of a member, that is, the name it takes on...
SCH_SHEET_PATH Sheet() const
long m_vector_start
Highest member of a vector bus.
wxString VectorPrefix() const
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
void SetSuffix(const wxString &aSuffix)
void SetPrefix(const wxString &aPrefix)
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
bool operator!=(const SCH_CONNECTION &aOther) const
SCH_ITEM * Parent() const
SCH_SHEET_PATH m_local_sheet
When a connection is overridden by one on a different hierarchical sheet, it will be cloned and m_she...
long VectorStart() const
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
static bool isSuperSubOverbar(wxChar c)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
long m_vector_index
Index of bus vector member nets.
long m_vector_end
Lowest member of a vector bus.
bool IsBus() const
bool IsSubsetOf(SCH_CONNECTION *aOther) const
Returns true if this connection is contained within aOther (but not the same as aOther)
wxString m_vector_prefix
void * m_lastDriver
WEAK POINTER (there is no guarantee it is still allocated) Equality comparisons are OK,...
CONNECTION_TYPE Type() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
SCH_ITEM * m_parent
The SCH_ITEM this connection is owned by.
bool operator==(const SCH_CONNECTION &aOther) const
Note: the equality operator for SCH_CONNECTION only tests the net properties, not the ownership / she...
This item represents a bus vector.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112