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