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  * @author Jon Evans <jon@craftyjon.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU 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 <regex>
22 #include <wx/tokenzr.h>
23 
24 #include <connection_graph.h>
25 #include <sch_component.h>
26 #include <sch_pin.h>
27 #include <sch_screen.h>
28 #include <advanced_config.h>
29 
30 #include <sch_connection.h>
31 
32 
62  m_sheet( aPath ),
63  m_parent( aParent ),
64  m_graph( nullptr )
65 {
66  Reset();
67 }
68 
69 
71  m_sheet( SCH_SHEET_PATH() ),
72  m_parent( nullptr ),
73  m_graph( aGraph )
74 {
75  Reset();
76 }
77 
78 
79 bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
80 {
81  // NOTE: Not comparing m_dirty or net/bus/subgraph codes
82  if( ( aOther.m_driver == m_driver ) &&
83  ( aOther.m_type == m_type ) &&
84  ( aOther.m_name == m_name ) &&
85  ( aOther.m_sheet == m_sheet ) )
86  {
87  return true;
88  }
89 
90  return false;
91 }
92 
93 
95 {
96  m_driver = aItem;
97 
98  recacheName();
99 
100  for( const auto& member : m_members )
101  member->SetDriver( aItem );
102 }
103 
104 
106 {
107  m_sheet = aSheet;
108 
109  recacheName();
110 
111  for( const auto& member : m_members )
112  member->SetSheet( aSheet );
113 }
114 
115 
116 bool SCH_CONNECTION::operator!=( const SCH_CONNECTION& aOther ) const
117 {
118  return !( aOther == *this );
119 }
120 
121 
122 void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
123 {
124  m_members.clear();
125 
126  m_name = aLabel;
127  m_local_name = aLabel;
128 
129  wxString prefix;
130  std::vector<wxString> members;
131 
132  wxString unescaped = UnescapeString( aLabel );
133 
134  if( ParseBusVector( unescaped, &prefix, &members ) )
135  {
137  m_vector_prefix = prefix;
138 
139  long i = 0;
140 
141  for( const wxString& vector_member : members )
142  {
143  auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
144  member->m_type = CONNECTION_TYPE::NET;
145  member->m_prefix = m_prefix;
146  member->m_local_name = vector_member;
147  member->m_vector_index = i++;
148  member->SetName( vector_member );
149  member->SetGraph( m_graph );
150  m_members.push_back( member );
151  }
152  }
153  else if( ParseBusGroup( unescaped, &prefix, &members ) )
154  {
156 
157  // Named bus groups generate a net prefix, unnamed ones don't
158  if( !prefix.IsEmpty() )
159  prefix += wxT( "." );
160 
161  for( const wxString& group_member : members )
162  {
163  // Handle alias inside bus group member list
164  if( auto alias = m_graph->GetBusAlias( group_member ) )
165  {
166  for( const wxString& alias_member : alias->Members() )
167  {
168  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
169  member->SetPrefix( prefix );
170  member->SetGraph( m_graph );
171  member->ConfigureFromLabel( alias_member );
172  m_members.push_back( member );
173  }
174  }
175  else
176  {
177  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
178  member->SetPrefix( prefix );
179  member->SetGraph( m_graph );
180  member->ConfigureFromLabel( group_member );
181  m_members.push_back( member );
182  }
183  }
184  }
185  else
186  {
188  }
189 
190  recacheName();
191 }
192 
193 
195 {
197  m_name.Empty();
198  m_local_name.Empty();
199  m_cached_name.Empty();
200  m_cached_name_with_path.Empty();
201  m_prefix.Empty();
202  m_suffix .Empty();
203  m_driver = nullptr;
204  m_members.clear();
205  m_dirty = true;
206  m_net_code = 0;
207  m_bus_code = 0;
208  m_subgraph_code = 0;
209  m_vector_start = 0;
210  m_vector_end = 0;
211  m_vector_index = 0;
212  m_vector_prefix.Empty();
213 }
214 
215 
217 {
218  m_graph = aOther.m_graph;
219  m_type = aOther.Type();
220  m_driver = aOther.Driver();
221  m_sheet = aOther.Sheet();
222  m_name = aOther.m_name;
223  // Note: m_local_name is not cloned
224  m_prefix = aOther.Prefix();
225  m_suffix = aOther.Suffix();
226  m_members = aOther.Members();
227  m_net_code = aOther.NetCode();
228  m_bus_code = aOther.BusCode();
229  m_vector_start = aOther.VectorStart();
230  m_vector_end = aOther.VectorEnd();
231  // Note: m_vector_index is not cloned
232  m_vector_prefix = aOther.VectorPrefix();
233 
234  // Note: subgraph code isn't cloned, it should remain with the original object
235 
236  recacheName();
237 }
238 
239 
241 {
242  wxASSERT( Parent() );
243 
244  switch( Parent()->Type() )
245  {
246  case SCH_LABEL_T:
247  case SCH_GLOBAL_LABEL_T:
248  case SCH_HIER_LABEL_T:
249  case SCH_SHEET_PIN_T:
250  case SCH_SHEET_T:
251  case LIB_PIN_T:
252  return true;
253 
254  case SCH_PIN_T:
255  {
256  auto pin = static_cast<SCH_PIN*>( Parent() );
257 
258  // Only annotated components should drive nets
259  return ( pin->IsPowerConnection()
260  || pin->GetParentComponent()->IsAnnotated( &m_sheet ) );
261  }
262 
263  default:
264  return false;
265  }
266 }
267 
268 
269 const wxString& SCH_CONNECTION::Name( bool aIgnoreSheet ) const
270 {
271  wxASSERT( !m_cached_name.IsEmpty() );
272  return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
273 }
274 
275 
277 {
278  m_cached_name = m_name.IsEmpty() ? "<NO NET>" : m_prefix + m_name + m_suffix;
279 
280  bool prepend_path = true;
281 
282  if( !Parent() || m_type == CONNECTION_TYPE::NONE )
283  prepend_path = false;
284 
285  if( m_driver )
286  {
287  switch( m_driver->Type() )
288  {
289  case SCH_GLOBAL_LABEL_T:
290  case SCH_PIN_T:
291  // Pins are either power connections or belong to a uniquely-annotated
292  // component, so they don't need a path if they are driving the subgraph
293  prepend_path = false;
294  break;
295 
296  default:
297  break;
298  }
299  }
300 
303 }
304 
305 
306 void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
307 {
308  m_prefix = aPrefix;
309 
310  recacheName();
311 
312  for( const auto& m : Members() )
313  m->SetPrefix( aPrefix );
314 }
315 
316 
317 void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
318 {
319  m_suffix = aSuffix;
320 
321  recacheName();
322 
323  for( const auto& m : Members() )
324  m->SetSuffix( aSuffix );
325 }
326 
327 
329 {
330  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
331  return;
332 
333  wxString msg, group_name;
334  std::vector<wxString> group_members;
335 
336  aList.push_back( MSG_PANEL_ITEM( _( "Connection Name" ), UnescapeString( Name() ), BROWN ) );
337 
338  // NOTE(JE) Disabling this for now, because net codes are generated in the netlist exporter
339  // in order to avoid sort costs. It may make sense to just tear out net codes from the
340  // CONNECTION_GRAPH entirely in the future, as they are mostly only useful for netlist exports.
341 #if 0
342  if( !IsBus() )
343  {
344  msg.Printf( "%d", m_net_code );
345  aList.push_back( MSG_PANEL_ITEM( _( "Net Code" ), msg, BROWN ) );
346  }
347 #endif
348 
349  if( auto alias = m_graph->GetBusAlias( m_name ) )
350  {
351  msg.Printf( _( "Bus Alias %s Members" ), m_name );
352 
353  wxString members;
354 
355  for( const auto& member : alias->Members() )
356  members << member << " ";
357 
358  aList.push_back( MSG_PANEL_ITEM( msg, members, RED ) );
359  }
360  else if( ParseBusGroup( m_name, &group_name, &group_members ) )
361  {
362  for( const auto& group_member : group_members )
363  {
364  if( auto group_alias = m_graph->GetBusAlias( group_member ) )
365  {
366  msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
367 
368  wxString members;
369 
370  for( const auto& member : group_alias->Members() )
371  members << member << " ";
372 
373  aList.push_back( MSG_PANEL_ITEM( msg, members, RED ) );
374  }
375  }
376  }
377 }
378 
379 
381 {
382  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
383  return;
384 
385  // These messages are not flagged as translatable, because they are only debug messages
386  wxString msg;
387 
388  AppendInfoToMsgPanel( aList );
389 
390  if( IsBus() )
391  {
392  msg.Printf( "%d", m_bus_code );
393  aList.push_back( MSG_PANEL_ITEM( "Bus Code", msg, BROWN ) );
394  }
395 
396  msg.Printf( "%d", m_subgraph_code );
397  aList.push_back( MSG_PANEL_ITEM( "Subgraph Code", msg, BROWN ) );
398 
399  if( auto driver = Driver() )
400  {
401  msg.Printf( "%s at %p", driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ), driver );
402  aList.push_back( MSG_PANEL_ITEM( "Connection Source", msg, RED ) );
403  }
404 }
405 
406 
407 bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
408 {
409  return ParseBusVector( aLabel, nullptr, nullptr ) || ParseBusGroup( aLabel, nullptr, nullptr );
410 }
411 
412 
413 bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
414 {
415  // Weak heuristic for performance reasons. Stronger test will be used for connectivity
416  wxString label = UnescapeString( aLabel );
417 
418  return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
419 }
420 
421 
422 static bool isSuperSub( wxChar c )
423 {
424  return c == '_' || c == '^';
425 };
426 
427 
428 bool SCH_CONNECTION::ParseBusVector( const wxString& aBus, wxString* aName,
429  std::vector<wxString>* aMemberList )
430 {
431  auto isDigit = []( wxChar c )
432  {
433  static wxString digits( wxT( "0123456789" ) );
434  return digits.Contains( c );
435  };
436 
437  size_t busLen = aBus.length();
438  size_t i = 0;
439  wxString prefix;
440  wxString suffix;
441  wxString tmp;
442  long begin = 0;
443  long end = 0;
444  int braceNesting = 0;
445 
446  prefix.reserve( busLen );
447 
448  // Parse prefix
449  //
450  for( ; i < busLen; ++i )
451  {
452  if( aBus[i] == '{' )
453  {
454  if( i > 0 && isSuperSub( aBus[i-1] ) )
455  braceNesting++;
456  else
457  return false;
458  }
459  else if( aBus[i] == '}' )
460  {
461  braceNesting--;
462  }
463 
464  if( aBus[i] == ' ' || aBus[i] == ']' )
465  return false;
466 
467  if( aBus[i] == '[' )
468  break;
469 
470  prefix += aBus[i];
471  }
472 
473  // Parse start number
474  //
475  i++; // '[' character
476 
477  if( i >= busLen )
478  return false;
479 
480  for( ; i < busLen; ++i )
481  {
482  if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
483  {
484  tmp.ToLong( &begin );
485  i += 2;
486  break;
487  }
488 
489  if( !isDigit( aBus[i] ) )
490  return false;
491 
492  tmp += aBus[i];
493  }
494 
495  // Parse end number
496  //
497  tmp = wxEmptyString;
498 
499  if( i >= busLen )
500  return false;
501 
502  for( ; i < busLen; ++i )
503  {
504  if( aBus[i] == ']' )
505  {
506  tmp.ToLong( &end );
507  ++i;
508  break;
509  }
510 
511  if( !isDigit( aBus[i] ) )
512  return false;
513 
514  tmp += aBus[i];
515  }
516 
517  // Parse suffix
518  //
519  for( ; i < busLen; ++i )
520  {
521  if( aBus[i] == '}' )
522  {
523  braceNesting--;
524  suffix += aBus[i];
525  }
526  else if( aBus[i] == '~' )
527  {
528  suffix += aBus[i];
529  }
530  else
531  {
532  return false;
533  }
534  }
535 
536  if( braceNesting != 0 )
537  return false;
538 
539  if( begin == end )
540  return false;
541  else if( begin > end )
542  std::swap( begin, end );
543 
544  if( aName )
545  *aName = prefix;
546 
547  if( aMemberList )
548  {
549  for( long idx = begin; idx <= end; ++idx )
550  {
551  wxString str = prefix;
552  str << idx;
553  str << suffix;
554 
555  aMemberList->emplace_back( str );
556  }
557  }
558 
559  return true;
560 }
561 
562 
563 bool SCH_CONNECTION::ParseBusGroup( wxString aGroup, wxString* aName,
564  std::vector<wxString>* aMemberList )
565 {
566  size_t groupLen = aGroup.length();
567  size_t i = 0;
568  wxString prefix;
569  wxString suffix;
570  wxString tmp;
571  int braceNesting = 0;
572 
573  prefix.reserve( groupLen );
574 
575  // Parse prefix
576  //
577  for( ; i < groupLen; ++i )
578  {
579  if( aGroup[i] == '{' )
580  {
581  if( i > 0 && isSuperSub( aGroup[i-1] ) )
582  braceNesting++;
583  else
584  break;
585  }
586  else if( aGroup[i] == '}' )
587  {
588  braceNesting--;
589  }
590 
591  if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
592  return false;
593 
594  prefix += aGroup[i];
595  }
596 
597  if( braceNesting != 0 )
598  return false;
599 
600  if( aName )
601  *aName = prefix;
602 
603  // Parse members
604  //
605  i++; // '{' character
606 
607  if( i >= groupLen )
608  return false;
609 
610  for( ; i < groupLen; ++i )
611  {
612  if( aGroup[i] == '{' )
613  {
614  if( i > 0 && isSuperSub( aGroup[i-1] ) )
615  braceNesting++;
616  else
617  return false;
618  }
619  else if( aGroup[i] == '}' )
620  {
621  if( braceNesting )
622  braceNesting--;
623  else
624  {
625  if( aMemberList )
626  aMemberList->push_back( tmp );
627 
628  return true;
629  }
630  }
631 
632  if( aGroup[i] == ' ' )
633  {
634  if( aMemberList )
635  aMemberList->push_back( tmp );
636 
637  tmp.Clear();
638  continue;
639  }
640 
641  tmp += aGroup[i];
642  }
643 
644  return false;
645 }
646 
647 
648 const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
649 {
650  std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
651 
652  for( const auto& member : m_members )
653  if( member->IsBus() )
654  ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
655 
656  return ret;
657 }
658 
659 
660 wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
661 {
662  size_t groupLen = aGroup.length();
663  size_t i = 0;
664  wxString ret;
665  int braceNesting = 0;
666  int tildeNesting = 0;
667 
668  // Parse prefix
669  //
670  for( ; i < groupLen; ++i )
671  {
672  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
673  {
674  braceNesting++;
675  i++;
676  continue;
677  }
678  else if( aGroup[i] == '~' )
679  {
680  if( tildeNesting )
681  {
682  tildeNesting = 0;
683  continue;
684  }
685  else
686  {
687  tildeNesting++;
688  }
689  }
690  else if( aGroup[i] == '}' )
691  {
692  braceNesting--;
693  continue;
694  }
695 
696  ret += aGroup[i];
697 
698  if( aGroup[i] == '{' )
699  break;
700  }
701 
702  // Parse members
703  //
704  i++; // '{' character
705 
706  for( ; i < groupLen; ++i )
707  {
708  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
709  {
710  braceNesting++;
711  i++;
712  continue;
713  }
714  else if( aGroup[i] == '~' )
715  {
716  if( tildeNesting )
717  {
718  tildeNesting = 0;
719  continue;
720  }
721  else
722  {
723  tildeNesting++;
724  }
725  }
726  else if( aGroup[i] == '}' )
727  {
728  braceNesting--;
729  continue;
730  }
731 
732  ret += aGroup[i];
733 
734  if( aGroup[i] == '}' )
735  break;
736  }
737 
738  return ret;
739 }
740 
741 
743 {
744  if( !aOther->IsBus() )
745  return false;
746 
747  for( const auto& member : aOther->Members() )
748  {
749  if( member->FullLocalName() == FullLocalName() )
750  return true;
751  }
752 
753  return false;
754 }
755 
756 
758 {
759  if( !aOther->IsBus() )
760  return false;
761 
762  auto me = Name( true );
763 
764  for( const auto& m : aOther->Members() )
765  if( m->Name( true ) == me )
766  return true;
767 
768  return false;
769 }
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.
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.
SCH_CONNECTION(SCH_ITEM *aParent=nullptr, SCH_SHEET_PATH aPath=SCH_SHEET_PATH())
Buses can be defined in multiple ways.
Definition: color4d.h:61
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)
int BusCode() const
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parses a bus vector (e.g.
CONNECTION_TYPE m_type
No connection to this item.
int NetCode() const
wxString Suffix() 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.
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
bool isDigit(char cc)
Definition: dsnlexer.cpp:466
long VectorEnd() const
void Reset()
Clears connectivity information.
SCH_ITEM * Driver() const
CONNECTION_GRAPH * m_graph
Pointer to the connection graph for the schematic this connection exists on.
const wxString & Name(bool aIgnoreSheet=false) const
SCH_SHEET_PATH.
wxString FullLocalName() const
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
Definition: color4d.h:59
SCH_SHEET_PATH m_sheet
The hierarchical sheet this connection is on.
void AppendDebugInfoToMsgPanel(MSG_PANEL_ITEMS &aList) const
Adds extended debug information about the connection object to aList.
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:131
void SetPrefix(const wxString &aPrefix)
bool operator!=(const SCH_CONNECTION &aOther) const
SCH_ITEM * Parent() const
std::vector< MSG_PANEL_ITEM > MSG_PANEL_ITEMS
Definition: msgpanel.h:102
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 of advanced config.
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:53
wxString PathHumanReadable() const
Function PathHumanReadable returns the sheet path in a human readable form, i.e.
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
CONNECTION_TYPE Type() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:186
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
Function Type()
Definition: base_struct.h:193