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 <jon@craftyjon.com>
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 <kicad_string.h>
33 
34 #include <sch_connection.h>
35 
65  m_sheet( aPath ),
66  m_parent( aParent ),
67  m_driver( nullptr ),
68  m_graph( nullptr )
69 {
70  Reset();
71 }
72 
73 
75  m_sheet( SCH_SHEET_PATH() ),
76  m_parent( nullptr ),
77  m_driver( nullptr ),
78  m_graph( aGraph )
79 {
80  Reset();
81 }
82 
83 
84 bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
85 {
86  // NOTE: Not comparing m_dirty or net/bus/subgraph codes
87  if( ( aOther.m_driver == m_driver ) &&
88  ( aOther.m_type == m_type ) &&
89  ( aOther.m_name == m_name ) &&
90  ( aOther.m_sheet == m_sheet ) )
91  {
92  return true;
93  }
94 
95  return false;
96 }
97 
98 
100 {
101  m_driver = aItem;
102 
103  recacheName();
104 
105  for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
106  member->SetDriver( aItem );
107 }
108 
109 
111 {
112  m_sheet = aSheet;
113 
114  recacheName();
115 
116  for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
117  member->SetSheet( aSheet );
118 }
119 
120 
121 bool SCH_CONNECTION::operator!=( const SCH_CONNECTION& aOther ) const
122 {
123  return !( aOther == *this );
124 }
125 
126 
127 void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
128 {
129  m_members.clear();
130 
131  m_name = aLabel;
132  m_local_name = aLabel;
134 
135  wxString prefix;
136  std::vector<wxString> members;
137 
138  wxString unescaped = UnescapeString( aLabel );
139 
140  if( NET_SETTINGS::ParseBusVector( unescaped, &prefix, &members ) )
141  {
143  m_vector_prefix = prefix;
144 
145  long i = 0;
146 
147  for( const wxString& vector_member : members )
148  {
149  auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
150  member->m_type = CONNECTION_TYPE::NET;
151  member->m_prefix = m_prefix;
152  member->m_local_name = vector_member;
153  member->m_local_prefix = m_prefix;
154  member->m_vector_index = i++;
155  member->SetName( vector_member );
156  member->SetGraph( m_graph );
157  m_members.push_back( member );
158  }
159  }
160  else if( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
161  {
163  m_bus_prefix = prefix;
164 
165  // Named bus groups generate a net prefix, unnamed ones don't
166  if( !prefix.IsEmpty() )
167  prefix += wxT( "." );
168 
169  for( const wxString& group_member : members )
170  {
171  // Handle alias inside bus group member list
172  if( auto alias = m_graph->GetBusAlias( group_member ) )
173  {
174  for( const wxString& alias_member : alias->Members() )
175  {
176  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
177  member->SetPrefix( prefix );
178  member->SetGraph( m_graph );
179  member->ConfigureFromLabel( alias_member );
180  m_members.push_back( member );
181  }
182  }
183  else
184  {
185  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
186  member->SetPrefix( prefix );
187  member->SetGraph( m_graph );
188  member->ConfigureFromLabel( group_member );
189  m_members.push_back( member );
190  }
191  }
192  }
193  else
194  {
196  }
197 
198  recacheName();
199 }
200 
201 
203 {
205  m_name.Empty();
206  m_local_name.Empty();
207  m_local_prefix.Empty();
208  m_cached_name.Empty();
209  m_cached_name_with_path.Empty();
210  m_prefix.Empty();
211  m_bus_prefix.Empty();
212  m_suffix .Empty();
214  m_driver = nullptr;
215  m_members.clear();
216  m_dirty = true;
217  m_net_code = 0;
218  m_bus_code = 0;
219  m_subgraph_code = 0;
220  m_vector_start = 0;
221  m_vector_end = 0;
222  m_vector_index = 0;
223  m_vector_prefix.Empty();
224 }
225 
226 
228 {
229  m_graph = aOther.m_graph;
230  // Note: m_lastDriver is not cloned as it needs to be the last driver of *this* connection
231  m_driver = aOther.Driver();
232  m_sheet = aOther.Sheet();
233  m_name = aOther.m_name;
234  // Note: m_local_name is not cloned if not set yet
235  if( m_local_name.IsEmpty() )
236  {
237  m_local_name = aOther.LocalName();
238  m_local_prefix = aOther.Prefix();
239  }
240 
241  m_prefix = aOther.Prefix();
242  // m_bus_prefix is not cloned; only used for local names
243  m_suffix = aOther.Suffix();
244  m_net_code = aOther.NetCode();
245  m_bus_code = aOther.BusCode();
246  m_vector_start = aOther.VectorStart();
247  m_vector_end = aOther.VectorEnd();
248  // Note: m_vector_index is not cloned
249  m_vector_prefix = aOther.VectorPrefix();
250 
251  // Note: subgraph code isn't cloned, it should remain with the original object
252 
253  // Handle vector bus members: make sure local names are preserved where possible
254  const std::vector<std::shared_ptr<SCH_CONNECTION>>& otherMembers = aOther.Members();
255 
256  if( m_type == CONNECTION_TYPE::BUS && aOther.Type() == CONNECTION_TYPE::BUS )
257  {
258  if( m_members.empty() )
259  {
260  m_members = otherMembers;
261  }
262  else
263  {
264  size_t cloneLimit = std::min( m_members.size(), otherMembers.size() );
265 
266  for( size_t i = 0; i < cloneLimit; ++i )
267  m_members[i]->Clone( *otherMembers[i] );
268  }
269  }
271  {
272  if( m_members.empty() )
273  {
274  m_members = otherMembers;
275  }
276  else
277  {
278  // TODO: refactor this once we support deep nesting
279  for( size_t i = 0; i < m_members.size(); ++i )
280  {
281  auto it = std::find_if( otherMembers.begin(), otherMembers.end(),
282  [&]( const std::shared_ptr<SCH_CONNECTION>& aTest )
283  {
284  return aTest->LocalName() == m_members[i]->LocalName();
285  } );
286 
287  if( it != otherMembers.end() )
288  m_members[i]->Clone( **it );
289  }
290  }
291  }
292 
293  m_type = aOther.Type();
294 
295  recacheName();
296 }
297 
298 
300 {
301  wxASSERT( Parent() );
302 
303  switch( Parent()->Type() )
304  {
305  case SCH_LABEL_T:
306  case SCH_GLOBAL_LABEL_T:
307  case SCH_HIER_LABEL_T:
308  case SCH_SHEET_PIN_T:
309  case SCH_SHEET_T:
310  case LIB_PIN_T:
311  return true;
312 
313  case SCH_PIN_T:
314  {
315  auto pin = static_cast<SCH_PIN*>( Parent() );
316 
317  // Only annotated components should drive nets
318  return pin->IsPowerConnection() || pin->GetParentSymbol()->IsAnnotated( &m_sheet );
319  }
320 
321  default:
322  return false;
323  }
324 }
325 
326 
328 {
329  return m_driver != m_lastDriver;
330 }
331 
332 
334 {
336 }
337 
338 
339 
340 wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
341 {
342  wxASSERT( !m_cached_name.IsEmpty() );
343  return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
344 }
345 
346 
348 {
349  m_cached_name = m_name.IsEmpty() ? "<NO NET>" : m_prefix + m_name + m_suffix;
350 
351  bool prepend_path = true;
352 
353  if( !Parent() || m_type == CONNECTION_TYPE::NONE )
354  prepend_path = false;
355 
356  if( m_driver )
357  {
358  switch( m_driver->Type() )
359  {
360  case SCH_GLOBAL_LABEL_T:
361  case SCH_PIN_T:
362  // Pins are either power connections or belong to a uniquely-annotated
363  // component, so they don't need a path if they are driving the subgraph
364  prepend_path = false;
365  break;
366 
367  default:
368  break;
369  }
370  }
371 
374 }
375 
376 
377 void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
378 {
379  m_prefix = aPrefix;
380 
381  recacheName();
382 
383  for( const auto& m : Members() )
384  m->SetPrefix( aPrefix );
385 }
386 
387 
388 void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
389 {
390  m_suffix = aSuffix;
391 
392  recacheName();
393 
394  for( const auto& m : Members() )
395  m->SetSuffix( aSuffix );
396 }
397 
398 
400 {
401  wxString msg, group_name;
402  std::vector<wxString> group_members;
403 
404  aList.push_back( MSG_PANEL_ITEM( _( "Connection Name" ), UnescapeString( Name() ) ) );
405 
406  // NOTE(JE) Disabling this for now, because net codes are generated in the netlist exporter
407  // in order to avoid sort costs. It may make sense to just tear out net codes from the
408  // CONNECTION_GRAPH entirely in the future, as they are mostly only useful for netlist exports.
409 #if 0
410  if( !IsBus() )
411  {
412  msg.Printf( "%d", m_net_code );
413  aList.push_back( MSG_PANEL_ITEM( _( "Net Code" ), msg ) );
414  }
415 #endif
416 
417  if( auto alias = m_graph->GetBusAlias( m_name ) )
418  {
419  msg.Printf( _( "Bus Alias %s Members" ), m_name );
420 
421  wxString members;
422 
423  for( const auto& member : alias->Members() )
424  members << member << " ";
425 
426  aList.push_back( MSG_PANEL_ITEM( msg, members ) );
427  }
428  else if( NET_SETTINGS::ParseBusGroup( m_name, &group_name, &group_members ) )
429  {
430  for( const auto& group_member : group_members )
431  {
432  if( auto group_alias = m_graph->GetBusAlias( group_member ) )
433  {
434  msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
435 
436  wxString members;
437 
438  for( const auto& member : group_alias->Members() )
439  members << member << " ";
440 
441  aList.push_back( MSG_PANEL_ITEM( msg, members ) );
442  }
443  }
444  }
445 
446 #if defined(DEBUG)
447  // These messages are not flagged as translatable, because they are only debug messages
448 
449  if( !ADVANCED_CFG::GetCfg().m_RealTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
450  return;
451 
452  if( IsBus() )
453  {
454  msg.Printf( "%d", m_bus_code );
455  aList.push_back( MSG_PANEL_ITEM( "Bus Code", msg ) );
456  }
457 
458  msg.Printf( "%d", m_subgraph_code );
459  aList.push_back( MSG_PANEL_ITEM( "Subgraph Code", msg ) );
460 
461  if( auto driver = Driver() )
462  {
463  msg.Printf( "%s at %p", driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ), driver );
464  aList.push_back( MSG_PANEL_ITEM( "Connection Source", msg ) );
465  }
466 #endif
467 }
468 
469 
470 bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
471 {
472  const wxString& unescaped = UnescapeString( aLabel );
473 
474  return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
475  || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
476 }
477 
478 
479 bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
480 {
481  // Weak heuristic for performance reasons. Stronger test will be used for connectivity
482  wxString label = UnescapeString( aLabel );
483 
484  return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
485 }
486 
487 
488 const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
489 {
490  std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
491 
492  for( const auto& member : m_members )
493  if( member->IsBus() )
494  ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
495 
496  return ret;
497 }
498 
499 
500 static bool isSuperSub( wxChar c )
501 {
502  return c == '_' || c == '^';
503 };
504 
505 
506 wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
507 {
508  size_t groupLen = aGroup.length();
509  size_t i = 0;
510  wxString ret;
511  int braceNesting = 0;
512  int tildeNesting = 0;
513 
514  // Parse prefix
515  //
516  for( ; i < groupLen; ++i )
517  {
518  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
519  {
520  braceNesting++;
521  i++;
522  continue;
523  }
524  else if( aGroup[i] == '~' )
525  {
526  if( tildeNesting )
527  {
528  tildeNesting = 0;
529  continue;
530  }
531  else
532  {
533  tildeNesting++;
534  }
535  }
536  else if( aGroup[i] == '}' )
537  {
538  braceNesting--;
539  continue;
540  }
541 
542  ret += aGroup[i];
543 
544  if( aGroup[i] == '{' )
545  break;
546  }
547 
548  // Parse members
549  //
550  i++; // '{' character
551 
552  for( ; i < groupLen; ++i )
553  {
554  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
555  {
556  braceNesting++;
557  i++;
558  continue;
559  }
560  else if( aGroup[i] == '~' )
561  {
562  if( tildeNesting )
563  {
564  tildeNesting = 0;
565  continue;
566  }
567  else
568  {
569  tildeNesting++;
570  }
571  }
572  else if( aGroup[i] == '}' )
573  {
574  braceNesting--;
575  continue;
576  }
577 
578  ret += aGroup[i];
579 
580  if( aGroup[i] == '}' )
581  break;
582  }
583 
584  return ret;
585 }
586 
587 
589 {
590  if( !aOther->IsBus() )
591  return false;
592 
593  for( const auto& member : aOther->Members() )
594  {
595  if( member->FullLocalName() == FullLocalName() )
596  return true;
597  }
598 
599  return false;
600 }
601 
602 
604 {
605  if( !aOther->IsBus() )
606  return false;
607 
608  auto me = Name( true );
609 
610  for( const auto& m : aOther->Members() )
611  if( m->Name( true ) == me )
612  return true;
613 
614  return false;
615 }
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
int BusCode() const
CONNECTION_TYPE m_type
No connection to this item.
int NetCode() const
static bool ParseBusGroup(wxString aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parses a bus group label into the name and a list of components.
wxString Suffix() const
wxString Name(bool aIgnoreSheet=false) const
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
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)
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.
This item represents a net.
wxString m_suffix
Name suffix (used only for disambiguation)
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
void AppendInfoToMsgPanel(MSG_PANEL_ITEMS &aList) const
Adds information about the connection object to aList.
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)
#define _(s)
Definition: 3d_actions.cpp:33
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:151
void SetPrefix(const wxString &aPrefix)
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parses a bus vector (e.g.
bool operator!=(const SCH_CONNECTION &aOther) const
SCH_ITEM * Parent() const
std::vector< MSG_PANEL_ITEM > MSG_PANEL_ITEMS
Definition: msgpanel.h:97
long VectorStart() const
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
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.
static bool isSuperSub(wxChar c)
long m_vector_end
Lowest member of a vector bus.
EDA_MSG_ITEM is used EDA_MSG_PANEL as the item type for displaying messages.
Definition: msgpanel.h:54
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:197
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:163