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 
60 static std::regex bus_label_re( "^([^[:space:]]+)(\\[[\\d]+\\.+[\\d]+\\])(~?)$" );
61 
62 static std::regex bus_group_label_re( "^([^[:space:]]+)?\\{((?:[^[:space:]]+(?:\\[[\\d]+\\.+[\\d]+\\])? ?)+)\\}$" );
63 
64 
66  m_sheet( aPath ),
67  m_parent( aParent )
68 {
69  Reset();
70 }
71 
72 
73 bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
74 {
75  // NOTE: Not comparing m_dirty or net/bus/subgraph codes
76  if( ( aOther.m_driver == m_driver ) &&
77  ( aOther.m_type == m_type ) &&
78  ( aOther.m_name == m_name ) &&
79  ( aOther.m_sheet == m_sheet ) )
80  {
81  return true;
82  }
83 
84  return false;
85 }
86 
87 
89 {
90  m_driver = aItem;
91 
92  for( const auto& member : m_members )
93  member->SetDriver( aItem );
94 }
95 
96 
98 {
99  m_sheet = aSheet;
100 
101  for( const auto& member : m_members )
102  member->SetSheet( aSheet );
103 }
104 
105 
106 bool SCH_CONNECTION::operator!=( const SCH_CONNECTION& aOther ) const
107 {
108  return !( aOther == *this );
109 }
110 
111 
112 void SCH_CONNECTION::ConfigureFromLabel( wxString aLabel )
113 {
114  m_members.clear();
115 
116  m_name = aLabel;
117  m_local_name = aLabel;
118 
119  wxString unescaped = UnescapeString( aLabel );
120 
121  if( IsBusVectorLabel( unescaped ) )
122  {
124 
125  std::vector<wxString> members;
126 
127  ParseBusVector( unescaped, &m_vector_prefix, members );
128  long i = 0;
129 
130  for( const auto& vector_member : members )
131  {
132  auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
133  member->m_type = CONNECTION_TYPE::NET;
134  member->m_prefix = m_prefix;
135  member->m_name = vector_member;
136  member->m_local_name = vector_member;
137  member->m_vector_index = i++;
138  m_members.push_back( member );
139  }
140  }
141  else if( IsBusGroupLabel( unescaped ) )
142  {
144 
145  std::vector<wxString> members;
146  wxString group_name;
147 
148  if( ParseBusGroup( unescaped, &group_name, members ) )
149  {
150  // Named bus groups generate a net prefix, unnamed ones don't
151  wxString prefix = group_name != wxT( "" ) ? ( group_name + wxT( "." ) ) : wxT( "" );
152 
153  for( const auto& group_member : members )
154  {
155  // Handle alias inside bus group member list
156  if( auto alias = g_ConnectionGraph->GetBusAlias( group_member ) )
157  {
158  for( const auto& alias_member : alias->Members() )
159  {
160  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
161  member->SetPrefix( prefix );
162  member->ConfigureFromLabel( alias_member );
163  m_members.push_back( member );
164  }
165  }
166  else
167  {
168  auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
169  member->SetPrefix( prefix );
170  member->ConfigureFromLabel( group_member );
171  m_members.push_back( member );
172  }
173  }
174  }
175  }
176  else
177  {
179  }
180 }
181 
182 
184 {
186  m_name.Empty();
187  m_local_name.Empty();
188  m_prefix.Empty();
189  m_suffix .Empty();
190  m_driver = nullptr;
191  m_members.clear();
192  m_dirty = true;
193  m_net_code = 0;
194  m_bus_code = 0;
195  m_subgraph_code = 0;
196  m_vector_start = 0;
197  m_vector_end = 0;
198  m_vector_index = 0;
199  m_vector_prefix.Empty();
200 }
201 
202 
204 {
205  m_type = aOther.Type();
206  m_driver = aOther.Driver();
207  m_sheet = aOther.Sheet();
208  m_name = aOther.m_name;
209  // Note: m_local_name is not cloned
210  m_prefix = aOther.Prefix();
211  m_suffix = aOther.Suffix();
212  m_members = aOther.Members();
213  m_net_code = aOther.NetCode();
214  m_bus_code = aOther.BusCode();
215  m_vector_start = aOther.VectorStart();
216  m_vector_end = aOther.VectorEnd();
217  // Note: m_vector_index is not cloned
218  m_vector_prefix = aOther.VectorPrefix();
219 
220  // Note: subgraph code isn't cloned, it should remain with the original object
221 }
222 
223 
225 {
226  wxASSERT( Parent() );
227 
228  switch( Parent()->Type() )
229  {
230  case SCH_LABEL_T:
231  case SCH_GLOBAL_LABEL_T:
232  case SCH_HIER_LABEL_T:
233  case SCH_SHEET_PIN_T:
234  case SCH_SHEET_T:
235  case LIB_PIN_T:
236  return true;
237 
238  case SCH_PIN_T:
239  {
240  auto pin = static_cast<SCH_PIN*>( Parent() );
241 
242  // Only annotated components should drive nets
243  return ( pin->IsPowerConnection()
244  || pin->GetParentComponent()->IsAnnotated( &m_sheet ) );
245  }
246 
247  default:
248  return false;
249  }
250 }
251 
252 
253 wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
254 {
255  wxString ret = m_prefix + m_name + m_suffix;
256 
257  if( m_name.IsEmpty() )
258  ret = "<NO NET>";
259 
260  if( !Parent() || m_type == CONNECTION_TYPE::NONE )
261  return ret;
262 
263  if( !aIgnoreSheet )
264  {
265  bool prepend_path = true;
266 
267  if( m_driver )
268  {
269  switch( m_driver->Type() )
270  {
271  case SCH_PIN_T:
272  // Pins are either power connections or belong to a uniquely-annotated
273  // component, so they don't need a path if they are driving the subgraph
274  prepend_path = false;
275  break;
276 
277  case SCH_GLOBAL_LABEL_T:
278  prepend_path = false;
279  break;
280 
281  default:
282  break;
283  }
284  }
285 
286  if( prepend_path )
287  ret = m_sheet.PathHumanReadable() + ret;
288  }
289 
290  return ret;
291 }
292 
293 
294 void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
295 {
296  m_prefix = aPrefix;
297 
298  for( const auto& m : Members() )
299  m->SetPrefix( aPrefix );
300 }
301 
302 
303 void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
304 {
305  m_suffix = aSuffix;
306 
307  for( const auto& m : Members() )
308  m->SetSuffix( aSuffix );
309 }
310 
311 
313 {
314  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
315  return;
316 
317  wxString msg, group_name;
318  std::vector<wxString> group_members;
319 
320  aList.push_back( MSG_PANEL_ITEM( _( "Connection Name" ), UnescapeString( Name() ), BROWN ) );
321 
322  // NOTE(JE) Disabling this for now, because net codes are generated in the netlist exporter
323  // in order to avoid sort costs. It may make sense to just tear out net codes from the
324  // CONNECTION_GRAPH entirely in the future, as they are mostly only useful for netlist exports.
325 #if 0
326  if( !IsBus() )
327  {
328  msg.Printf( "%d", m_net_code );
329  aList.push_back( MSG_PANEL_ITEM( _( "Net Code" ), msg, BROWN ) );
330  }
331 #endif
332 
333  if( auto alias = g_ConnectionGraph->GetBusAlias( m_name ) )
334  {
335  msg.Printf( _( "Bus Alias %s Members" ), m_name );
336 
337  wxString members;
338 
339  for( const auto& member : alias->Members() )
340  members << member << " ";
341 
342  aList.push_back( MSG_PANEL_ITEM( msg, members, RED ) );
343  }
344  else if( ParseBusGroup( m_name, &group_name, group_members ) )
345  {
346  for( const auto& group_member : group_members )
347  {
348  if( auto group_alias = g_ConnectionGraph->GetBusAlias( group_member ) )
349  {
350  msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
351 
352  wxString members;
353 
354  for( const auto& member : group_alias->Members() )
355  members << member << " ";
356 
357  aList.push_back( MSG_PANEL_ITEM( msg, members, RED ) );
358  }
359  }
360  }
361 }
362 
363 
365 {
366  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
367  return;
368 
369  // These messages are not flagged as translatable, because they are only debug messages
370  wxString msg;
371 
372  AppendInfoToMsgPanel( aList );
373 
374  if( IsBus() )
375  {
376  msg.Printf( "%d", m_bus_code );
377  aList.push_back( MSG_PANEL_ITEM( "Bus Code", msg, BROWN ) );
378  }
379 
380  msg.Printf( "%d", m_subgraph_code );
381  aList.push_back( MSG_PANEL_ITEM( "Subgraph Code", msg, BROWN ) );
382 
383  if( auto driver = Driver() )
384  {
385  msg.Printf( "%s at %p", driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ), driver );
386  aList.push_back( MSG_PANEL_ITEM( "Connection Source", msg, RED ) );
387  }
388 }
389 
390 
391 bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
392 {
393  return IsBusVectorLabel( aLabel ) || IsBusGroupLabel( aLabel );
394 }
395 
396 
397 bool SCH_CONNECTION::IsBusVectorLabel( const wxString& aLabel )
398 {
399  try
400  {
401  return std::regex_match( std::string( aLabel.mb_str() ), bus_label_re );
402  }
403  catch( ... )
404  {
405  return false;
406  }
407 }
408 
409 
410 bool SCH_CONNECTION::IsBusGroupLabel( const wxString& aLabel )
411 {
412  try
413  {
414  return std::regex_match( std::string( aLabel.mb_str() ), bus_group_label_re );
415  }
416  catch( ... )
417  {
418  return false;
419  }
420 }
421 
422 
423 bool SCH_CONNECTION::ParseBusVector( wxString aBus, wxString* aName,
424  std::vector<wxString>& aMemberList ) const
425 {
426  auto ss_vector = std::string( aBus.mb_str() );
427  std::smatch matches;
428 
429  try
430  {
431  if( !std::regex_match( ss_vector, matches, bus_label_re ) )
432  return false;
433  }
434  catch( ... )
435  {
436  return false;
437  }
438 
439  long begin = 0, end = 0;
440  *aName = wxString( matches[1] );
441  wxString numberString( matches[2] );
442 
443  // If we have three match groups, it means there was a tilde at the end of the vector
444  bool append_tilde = wxString( matches[3] ).IsSameAs( wxT( "~" ) );
445 
446  // numberString will include the brackets, e.g. [5..0] so skip the first one
447  size_t i = 1, len = numberString.Len();
448  wxString tmp;
449 
450  while( i < len && numberString[i] != '.' )
451  {
452  tmp.Append( numberString[i] );
453  i++;
454  }
455 
456  tmp.ToLong( &begin );
457 
458  while( i < len && numberString[i] == '.' )
459  i++;
460 
461  tmp.Empty();
462 
463  while( i < len && numberString[i] != ']' )
464  {
465  tmp.Append( numberString[i] );
466  i++;
467  }
468 
469  tmp.ToLong( &end );
470 
471  if( begin < 0 )
472  begin = 0;
473 
474  if( end < 0 )
475  end = 0;
476 
477  if( begin > end )
478  std::swap( begin, end );
479 
480  for( long idx = begin; idx <= end; ++idx )
481  {
482  wxString str = *aName;
483  str << idx;
484 
485  if( append_tilde )
486  str << '~';
487 
488  aMemberList.emplace_back( str );
489  }
490 
491  return true;
492 }
493 
494 
495 bool SCH_CONNECTION::ParseBusGroup( wxString aGroup, wxString* aName,
496  std::vector<wxString>& aMemberList ) const
497 {
498  auto ss_group = std::string( aGroup.mb_str() );
499  std::smatch matches;
500 
501  try
502  {
503  if( !std::regex_match( ss_group, matches, bus_group_label_re ) )
504  {
505  return false;
506  }
507  }
508  catch( ... )
509  {
510  return false;
511  }
512 
513  *aName = wxString( matches[1] );
514 
515  wxStringTokenizer tokenizer( wxString( matches[2] ), " " );
516  while( tokenizer.HasMoreTokens() )
517  {
518  aMemberList.push_back( tokenizer.GetNextToken() );
519  }
520 
521  return true;
522 }
523 
524 
526 {
527  if( aOther->IsNet() )
528  return IsNet() ? ( aOther->Name( true ) == Name( true ) ) : false;
529 
530  if( !IsBus() )
531  return false;
532 
533  std::vector<wxString> mine, theirs;
534 
535  for( const auto& m : Members() )
536  mine.push_back( m->Name( true ) );
537 
538  for( const auto& m : aOther->Members() )
539  theirs.push_back( m->Name( true ) );
540 
541  std::set<wxString> subset;
542 
543  std::set_intersection( mine.begin(), mine.end(),
544  theirs.begin(), theirs.end(),
545  std::inserter(subset, subset.begin() ) );
546 
547  return ( !subset.empty() );
548 }
549 
550 
552 {
553  if( !aOther->IsBus() )
554  return false;
555 
556  auto me = Name( true );
557 
558  for( const auto& m : aOther->Members() )
559  if( m->Name( true ) == me )
560  return true;
561 
562  return false;
563 }
int m_subgraph_code
Groups directly-connected items.
static std::regex bus_label_re("^([^[:space:]]+)(\\[[\\d]+\\.+[\\d]+\\])(~?)$")
Buses can be defined in multiple ways.
wxString Prefix() const
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)
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())
bool IsMemberOfBus(SCH_CONNECTION *aOther) const
Returns true if this connection is a member of bus connection aOther.
void SetDriver(SCH_ITEM *aItem)
void SetSheet(SCH_SHEET_PATH aSheet)
CONNECTION_GRAPH * g_ConnectionGraph
This also wants to live in the eventual SCHEMATIC object.
int BusCode() const
CONNECTION_TYPE m_type
No connection to this item.
int NetCode() const
wxString Suffix() const
wxString Name(bool aIgnoreSheet=false) const
long VectorEnd() const
void Reset()
Clears connectivity information.
SCH_ITEM * Driver() const
void ConfigureFromLabel(wxString aLabel)
Configures the connection given a label.
static bool IsBusVectorLabel(const wxString &aLabel)
Test if aLabel has a bus vector notation (simple bus, e.g.
Definition: colors.h:60
SCH_SHEET_PATH.
bool ParseBusGroup(wxString aGroup, wxString *name, std::vector< wxString > &aMemberList) const
Parses a bus group label into the name and a list of components.
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,...
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.
bool IsNet() const
long m_vector_start
Highest member of a vector bus.
static std::regex bus_group_label_re("^([^[:space:]]+)?\\{((?:[^[:space:]]+(?:\\[[\\d]+\\.+[\\d]+\\])? ?)+)\\}$")
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 ParseBusVector(wxString aBus, wxString *aName, std::vector< wxString > &aMemberList) const
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: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.
long m_vector_end
Lowest member of a vector bus.
static bool IsBusGroupLabel(const wxString &aLabel)
Test if aLabel has a bus group notation.
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 aOther is a subset of this connection or vice versa.
wxString m_vector_prefix
CONNECTION_TYPE Type() const
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
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:212
Definition: colors.h:62