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 <project/net_settings.h>
29 #include <advanced_config.h>
30 
31 #include <sch_connection.h>
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( NET_SETTINGS::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( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
154  {
156  m_bus_prefix = prefix;
157 
158  // Named bus groups generate a net prefix, unnamed ones don't
159  if( !prefix.IsEmpty() )
160  prefix += wxT( "." );
161 
162  for( const wxString& group_member : members )
163  {
164  // Handle alias inside bus group member list
165  if( auto alias = m_graph->GetBusAlias( group_member ) )
166  {
167  for( const wxString& alias_member : alias->Members() )
168  {
169  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
170  member->SetPrefix( prefix );
171  member->SetGraph( m_graph );
172  member->ConfigureFromLabel( alias_member );
173  m_members.push_back( member );
174  }
175  }
176  else
177  {
178  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
179  member->SetPrefix( prefix );
180  member->SetGraph( m_graph );
181  member->ConfigureFromLabel( group_member );
182  m_members.push_back( member );
183  }
184  }
185  }
186  else
187  {
189  }
190 
191  recacheName();
192 }
193 
194 
196 {
198  m_name.Empty();
199  m_local_name.Empty();
200  m_cached_name.Empty();
201  m_cached_name_with_path.Empty();
202  m_prefix.Empty();
203  m_bus_prefix.Empty();
204  m_suffix .Empty();
205  m_driver = nullptr;
206  m_members.clear();
207  m_dirty = true;
208  m_net_code = 0;
209  m_bus_code = 0;
210  m_subgraph_code = 0;
211  m_vector_start = 0;
212  m_vector_end = 0;
213  m_vector_index = 0;
214  m_vector_prefix.Empty();
215 }
216 
217 
219 {
220  m_graph = aOther.m_graph;
221  m_type = aOther.Type();
222  m_driver = aOther.Driver();
223  m_sheet = aOther.Sheet();
224  m_name = aOther.m_name;
225  // Note: m_local_name is not cloned
226  m_prefix = aOther.Prefix();
227  m_bus_prefix = aOther.BusPrefix();
228  m_suffix = aOther.Suffix();
229  m_members = aOther.Members();
230  m_net_code = aOther.NetCode();
231  m_bus_code = aOther.BusCode();
232  m_vector_start = aOther.VectorStart();
233  m_vector_end = aOther.VectorEnd();
234  // Note: m_vector_index is not cloned
235  m_vector_prefix = aOther.VectorPrefix();
236 
237  // Note: subgraph code isn't cloned, it should remain with the original object
238 
239  recacheName();
240 }
241 
242 
244 {
245  wxASSERT( Parent() );
246 
247  switch( Parent()->Type() )
248  {
249  case SCH_LABEL_T:
250  case SCH_GLOBAL_LABEL_T:
251  case SCH_HIER_LABEL_T:
252  case SCH_SHEET_PIN_T:
253  case SCH_SHEET_T:
254  case LIB_PIN_T:
255  return true;
256 
257  case SCH_PIN_T:
258  {
259  auto pin = static_cast<SCH_PIN*>( Parent() );
260 
261  // Only annotated components should drive nets
262  return ( pin->IsPowerConnection()
263  || pin->GetParentComponent()->IsAnnotated( &m_sheet ) );
264  }
265 
266  default:
267  return false;
268  }
269 }
270 
271 
272 wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
273 {
274  wxASSERT( !m_cached_name.IsEmpty() );
275  return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
276 }
277 
278 
280 {
281  m_cached_name = m_name.IsEmpty() ? "<NO NET>" : m_prefix + m_name + m_suffix;
282 
283  bool prepend_path = true;
284 
285  if( !Parent() || m_type == CONNECTION_TYPE::NONE )
286  prepend_path = false;
287 
288  if( m_driver )
289  {
290  switch( m_driver->Type() )
291  {
292  case SCH_GLOBAL_LABEL_T:
293  case SCH_PIN_T:
294  // Pins are either power connections or belong to a uniquely-annotated
295  // component, so they don't need a path if they are driving the subgraph
296  prepend_path = false;
297  break;
298 
299  default:
300  break;
301  }
302  }
303 
306 }
307 
308 
309 void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
310 {
311  m_prefix = aPrefix;
312 
313  recacheName();
314 
315  for( const auto& m : Members() )
316  m->SetPrefix( aPrefix );
317 }
318 
319 
320 void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
321 {
322  m_suffix = aSuffix;
323 
324  recacheName();
325 
326  for( const auto& m : Members() )
327  m->SetSuffix( aSuffix );
328 }
329 
330 
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( NET_SETTINGS::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 #if defined(DEBUG)
379  // These messages are not flagged as translatable, because they are only debug messages
380 
381  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
382  return;
383 
384  if( IsBus() )
385  {
386  msg.Printf( "%d", m_bus_code );
387  aList.push_back( MSG_PANEL_ITEM( "Bus Code", msg, BROWN ) );
388  }
389 
390  msg.Printf( "%d", m_subgraph_code );
391  aList.push_back( MSG_PANEL_ITEM( "Subgraph Code", msg, BROWN ) );
392 
393  if( auto driver = Driver() )
394  {
395  msg.Printf( "%s at %p", driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ), driver );
396  aList.push_back( MSG_PANEL_ITEM( "Connection Source", msg, RED ) );
397  }
398 #endif
399 }
400 
401 
402 bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
403 {
404  const wxString& unescaped = UnescapeString( aLabel );
405 
406  return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
407  || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
408 }
409 
410 
411 bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
412 {
413  // Weak heuristic for performance reasons. Stronger test will be used for connectivity
414  wxString label = UnescapeString( aLabel );
415 
416  return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
417 }
418 
419 
420 const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
421 {
422  std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
423 
424  for( const auto& member : m_members )
425  if( member->IsBus() )
426  ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
427 
428  return ret;
429 }
430 
431 
432 static bool isSuperSub( wxChar c )
433 {
434  return c == '_' || c == '^';
435 };
436 
437 
438 wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
439 {
440  size_t groupLen = aGroup.length();
441  size_t i = 0;
442  wxString ret;
443  int braceNesting = 0;
444  int tildeNesting = 0;
445 
446  // Parse prefix
447  //
448  for( ; i < groupLen; ++i )
449  {
450  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
451  {
452  braceNesting++;
453  i++;
454  continue;
455  }
456  else if( aGroup[i] == '~' )
457  {
458  if( tildeNesting )
459  {
460  tildeNesting = 0;
461  continue;
462  }
463  else
464  {
465  tildeNesting++;
466  }
467  }
468  else if( aGroup[i] == '}' )
469  {
470  braceNesting--;
471  continue;
472  }
473 
474  ret += aGroup[i];
475 
476  if( aGroup[i] == '{' )
477  break;
478  }
479 
480  // Parse members
481  //
482  i++; // '{' character
483 
484  for( ; i < groupLen; ++i )
485  {
486  if( isSuperSub( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
487  {
488  braceNesting++;
489  i++;
490  continue;
491  }
492  else if( aGroup[i] == '~' )
493  {
494  if( tildeNesting )
495  {
496  tildeNesting = 0;
497  continue;
498  }
499  else
500  {
501  tildeNesting++;
502  }
503  }
504  else if( aGroup[i] == '}' )
505  {
506  braceNesting--;
507  continue;
508  }
509 
510  ret += aGroup[i];
511 
512  if( aGroup[i] == '}' )
513  break;
514  }
515 
516  return ret;
517 }
518 
519 
521 {
522  if( !aOther->IsBus() )
523  return false;
524 
525  for( const auto& member : aOther->Members() )
526  {
527  if( member->FullLocalName() == FullLocalName() )
528  return true;
529  }
530 
531  return false;
532 }
533 
534 
536 {
537  if( !aOther->IsBus() )
538  return false;
539 
540  auto me = Name( true );
541 
542  for( const auto& m : aOther->Members() )
543  if( m->Name( true ) == me )
544  return true;
545 
546  return false;
547 }
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.
wxString m_bus_prefix
Optional prefix of a bux group (always empty for nets and vector buses)
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
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.
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.
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.
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:152
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
wxString BusPrefix() 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:194
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