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-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Jon Evans <[email protected]>
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>
31#include <advanced_config.h>
32#include <string_utils.h>
33
34#include <sch_connection.h>
35#include <boost/algorithm/string/join.hpp>
36
66 m_sheet( aPath ),
67 m_local_sheet( aPath ),
68 m_parent( aParent ),
69 m_driver( nullptr ),
70 m_graph( nullptr )
71{
72 Reset();
73}
74
75
77 m_sheet( SCH_SHEET_PATH() ),
78 m_local_sheet( SCH_SHEET_PATH() ),
79 m_parent( nullptr ),
80 m_driver( nullptr ),
81 m_graph( aGraph )
82{
83 Reset();
84}
85
86
87bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
88{
89 // NOTE: Not comparing m_dirty or net/bus/subgraph codes
90 if( ( aOther.m_driver == m_driver ) &&
91 ( aOther.m_type == m_type ) &&
92 ( aOther.m_name == m_name ) &&
93 ( aOther.m_sheet == m_sheet ) )
94 {
95 return true;
96 }
97
98 return false;
99}
100
101
103{
104 m_driver = aItem;
105
106 recacheName();
107
108 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
109 member->SetDriver( aItem );
110}
111
112
114{
115 m_sheet = aSheet;
116 m_local_sheet = aSheet;
117
118 recacheName();
119
120 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
121 member->SetSheet( aSheet );
122}
123
124
126{
127 return !( aOther == *this );
128}
129
130
131void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
132{
133 m_members.clear();
134
135 m_name = aLabel;
136 m_local_name = aLabel;
138
139 wxString prefix;
140 std::vector<wxString> members;
141
142 wxString unescaped = UnescapeString( aLabel );
143
144 if( NET_SETTINGS::ParseBusVector( unescaped, &prefix, &members ) )
145 {
147 m_vector_prefix = prefix;
148
149 long i = 0;
150
151 for( const wxString& vector_member : members )
152 {
153 auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
154 member->m_type = CONNECTION_TYPE::NET;
155 member->m_prefix = m_prefix;
156 member->m_local_name = vector_member;
157 member->m_local_prefix = m_prefix;
158 member->m_vector_index = i++;
159 member->SetName( vector_member );
160 member->SetGraph( m_graph );
161 m_members.push_back( member );
162 }
163 }
164 else if( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
165 {
167 m_bus_prefix = prefix;
168
169 // Named bus groups generate a net prefix, unnamed ones don't
170 if( !prefix.IsEmpty() )
171 prefix += wxT( "." );
172
173 for( const wxString& group_member : members )
174 {
175 // Handle alias inside bus group member list
176 if( auto alias = m_graph->GetBusAlias( group_member ) )
177 {
178 for( const wxString& alias_member : alias->Members() )
179 {
180 auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
181 member->SetPrefix( prefix );
182 member->SetGraph( m_graph );
183 member->ConfigureFromLabel( alias_member );
184 m_members.push_back( member );
185 }
186 }
187 else
188 {
189 auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
190 member->SetPrefix( prefix );
191 member->SetGraph( m_graph );
192 member->ConfigureFromLabel( group_member );
193 m_members.push_back( member );
194 }
195 }
196 }
197 else
198 {
200 }
201
202 recacheName();
203}
204
205
207{
209 m_name.Empty();
210 m_local_name.Empty();
211 m_local_prefix.Empty();
212 m_cached_name.Empty();
214 m_prefix.Empty();
215 m_bus_prefix.Empty();
216 m_suffix .Empty();
218 m_driver = nullptr;
219 m_members.clear();
220 m_dirty = true;
221 m_net_code = 0;
222 m_bus_code = 0;
223 m_subgraph_code = 0;
224 m_vector_start = 0;
225 m_vector_end = 0;
226 m_vector_index = 0;
227 m_vector_prefix.Empty();
228}
229
230
232{
233 m_graph = aOther.m_graph;
234 // Note: m_lastDriver is not cloned as it needs to be the last driver of *this* connection
235 m_driver = aOther.Driver();
236 m_sheet = aOther.Sheet();
237 // Note: m_local_sheet is not cloned
238 m_name = aOther.m_name;
239 // Note: m_local_name is not cloned if not set yet
240 if( m_local_name.IsEmpty() )
241 {
242 m_local_name = aOther.LocalName();
243 m_local_prefix = aOther.Prefix();
244 }
245
246 m_prefix = aOther.Prefix();
247 // m_bus_prefix is not cloned; only used for local names
248 m_suffix = aOther.Suffix();
249 m_net_code = aOther.NetCode();
250 m_bus_code = aOther.BusCode();
251 m_vector_start = aOther.VectorStart();
252 m_vector_end = aOther.VectorEnd();
253 // Note: m_vector_index is not cloned
254 m_vector_prefix = aOther.VectorPrefix();
255
256 // Note: subgraph code isn't cloned, it should remain with the original object
257
258 // Handle vector bus members: make sure local names are preserved where possible
259 const std::vector<std::shared_ptr<SCH_CONNECTION>>& otherMembers = aOther.Members();
260
262 {
263 if( m_members.empty() )
264 {
265 m_members = otherMembers;
266 }
267 else
268 {
269 size_t cloneLimit = std::min( m_members.size(), otherMembers.size() );
270
271 for( size_t i = 0; i < cloneLimit; ++i )
272 m_members[i]->Clone( *otherMembers[i] );
273 }
274 }
276 {
277 if( m_members.empty() )
278 {
279 m_members = otherMembers;
280 }
281 else
282 {
283 // TODO: refactor this once we support deep nesting
284 for( size_t i = 0; i < m_members.size(); ++i )
285 {
286 auto it = std::find_if( otherMembers.begin(), otherMembers.end(),
287 [&]( const std::shared_ptr<SCH_CONNECTION>& aTest )
288 {
289 return aTest->LocalName() == m_members[i]->LocalName();
290 } );
291
292 if( it != otherMembers.end() )
293 m_members[i]->Clone( **it );
294 }
295 }
296 }
297
298 m_type = aOther.Type();
299
300 recacheName();
301}
302
303
305{
306 wxASSERT( Parent() );
307
308 switch( Parent()->Type() )
309 {
310 case SCH_LABEL_T:
312 case SCH_HIER_LABEL_T:
313 case SCH_SHEET_PIN_T:
314 case SCH_SHEET_T:
315 case LIB_PIN_T:
316 return true;
317
318 case SCH_PIN_T:
319 {
320 auto pin = static_cast<SCH_PIN*>( Parent() );
321
322 // Only annotated symbols should drive nets.
323 return pin->IsPowerConnection() || pin->GetParentSymbol()->IsAnnotated( &m_sheet );
324 }
325
326 default:
327 return false;
328 }
329}
330
331
333{
334 return m_driver != m_lastDriver;
335}
336
337
339{
341}
342
343
344
345wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
346{
347 wxASSERT( !m_cached_name.IsEmpty() );
348 return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
349}
350
351
353{
355 m_name.IsEmpty() ? wxT( "<NO NET>" ) : wxString( m_prefix ) << m_name << m_suffix;
356
357 bool prepend_path = true;
358
359 if( !Parent() || m_type == CONNECTION_TYPE::NONE )
360 prepend_path = false;
361
362 if( m_driver )
363 {
364 switch( m_driver->Type() )
365 {
367 case SCH_PIN_T:
368 // Pins are either power connections or belong to a uniquely-annotated
369 // symbol, so they don't need a path if they are driving the subgraph.
370 prepend_path = false;
371 break;
372
373 default:
374 break;
375 }
376 }
377
380}
381
382
383void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
384{
385 m_prefix = aPrefix;
386
387 recacheName();
388
389 for( const auto& m : Members() )
390 m->SetPrefix( aPrefix );
391}
392
393
394void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
395{
396 m_suffix = aSuffix;
397
398 recacheName();
399
400 for( const auto& m : Members() )
401 m->SetSuffix( aSuffix );
402}
403
404
405void SCH_CONNECTION::AppendInfoToMsgPanel( std::vector<MSG_PANEL_ITEM>& aList ) const
406{
407 wxString msg, group_name, members;
408 std::vector<wxString> group_members;
409
410 aList.emplace_back( _( "Connection Name" ), UnescapeString( Name() ) );
411
412 if( std::shared_ptr<BUS_ALIAS> alias = m_graph->GetBusAlias( m_name ) )
413 {
414 msg.Printf( _( "Bus Alias %s Members" ), m_name );
415 aList.emplace_back( msg, boost::algorithm::join( alias->Members(), " " ) );
416 }
417 else if( NET_SETTINGS::ParseBusGroup( m_name, &group_name, &group_members ) )
418 {
419 for( const wxString& group_member : group_members )
420 {
421 if( std::shared_ptr<BUS_ALIAS> group_alias = m_graph->GetBusAlias( group_member ) )
422 {
423 msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
424 aList.emplace_back( msg, boost::algorithm::join( group_alias->Members(), " " ) );
425 }
426 }
427 }
428
429#if defined(DEBUG)
430 // These messages are not flagged as translatable, because they are only debug messages
431
432 if( !ADVANCED_CFG::GetCfg().m_RealTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
433 return;
434
435 if( IsBus() )
436 aList.emplace_back( wxT( "Bus Code" ), wxString::Format( "%d", m_bus_code ) );
437
438 aList.emplace_back( wxT( "Subgraph Code" ), wxString::Format( "%d", m_subgraph_code ) );
439
440 if( SCH_ITEM* driver = Driver() )
441 {
443
444 msg.Printf( "%s at %p", driver->GetSelectMenuText( &unitsProvider ), driver );
445 aList.emplace_back( wxT( "Connection Source" ), msg );
446 }
447#endif
448}
449
450
451bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
452{
453 const wxString& unescaped = UnescapeString( aLabel );
454
455 return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
456 || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
457}
458
459
460bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
461{
462 // Weak heuristic for performance reasons. Stronger test will be used for connectivity
463 wxString label = UnescapeString( aLabel );
464
465 return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
466}
467
468
469const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
470{
471 std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
472
473 for( const auto& member : m_members )
474 if( member->IsBus() )
475 ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
476
477 return ret;
478}
479
480
481static bool isSuperSubOverbar( wxChar c )
482{
483 return c == '_' || c == '^' || c == '~';
484};
485
486
487wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
488{
489 size_t groupLen = aGroup.length();
490 size_t i = 0;
491 wxString ret;
492 int braceNesting = 0;
493
494 // Parse prefix
495 //
496 for( ; i < groupLen; ++i )
497 {
498 if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
499 {
500 braceNesting++;
501 i++;
502 continue;
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 // Parse members
517 //
518 i++; // '{' character
519
520 for( ; i < groupLen; ++i )
521 {
522 if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
523 {
524 braceNesting++;
525 i++;
526 continue;
527 }
528 else if( aGroup[i] == '}' )
529 {
530 braceNesting--;
531 continue;
532 }
533
534 ret += aGroup[i];
535
536 if( aGroup[i] == '}' )
537 break;
538 }
539
540 return ret;
541}
542
543
545{
546 if( !aOther->IsBus() )
547 return false;
548
549 for( const auto& member : aOther->Members() )
550 {
551 if( member->FullLocalName() == FullLocalName() )
552 return true;
553 }
554
555 return false;
556}
557
558
560{
561 if( !aOther->IsBus() )
562 return false;
563
564 auto me = Name( true );
565
566 for( const auto& m : aOther->Members() )
567 if( m->Name( true ) == me )
568 return true;
569
570 return false;
571}
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:111
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Calculates the connectivity of a schematic and generates netlists.
static bool m_allowRealTime
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Returns a bus alias pointer for the given name if it exists (from cache)
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parse a bus group label into the name and a list of components.
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
long VectorEnd() const
wxString FullLocalName() const
long m_vector_start
Highest member of a vector bus.
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.
SCH_ITEM * m_parent
The SCH_ITEM this connection is owned by.
SCH_SHEET_PATH m_sheet
The hierarchical sheet this connection is on.
bool operator!=(const SCH_CONNECTION &aOther) const
void SetSuffix(const wxString &aSuffix)
long VectorStart() const
wxString m_name
Name of the connection.
SCH_ITEM * Parent() const
long m_vector_end
Lowest member of a vector bus.
SCH_SHEET_PATH Sheet() const
CONNECTION_TYPE Type() const
bool HasDriverChanged() const
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
void Reset()
Clears connectivity information.
int BusCode() const
bool operator==(const SCH_CONNECTION &aOther) const
Note: the equality operator for SCH_CONNECTION only tests the net properties, not the ownership / she...
SCH_CONNECTION(SCH_ITEM *aParent=nullptr, SCH_SHEET_PATH aPath=SCH_SHEET_PATH())
Buses can be defined in multiple ways.
std::vector< std::shared_ptr< SCH_CONNECTION > > m_members
For bus connections, store a list of member connections.
wxString m_local_prefix
Local prefix for group bus members (used with m_local_name)
CONNECTION_TYPE m_type
SCH_ITEM * m_driver
The SCH_ITEM that drives this connection's net.
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
void SetPrefix(const wxString &aPrefix)
wxString LocalName() const
wxString Name(bool aIgnoreSheet=false) const
bool IsSubsetOf(SCH_CONNECTION *aOther) const
Returns true if this connection is contained within aOther (but not the same as aOther)
wxString Suffix() const
void SetDriver(SCH_ITEM *aItem)
SCH_SHEET_PATH m_local_sheet
When a connection is overridden by one on a different hierarchical sheet, it will be cloned and m_she...
bool IsBus() const
wxString m_prefix
Prefix if connection is member of a labeled bus group (or "" if not)
void Clone(const SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
void * m_lastDriver
WEAK POINTER (there is no guarantee it is still allocated) Equality comparisons are OK,...
wxString m_cached_name_with_path
Full name including sheet path (if not global)
long m_vector_index
Index of bus vector member nets.
wxString VectorPrefix() const
SCH_ITEM * Driver() const
void AppendInfoToMsgPanel(std::vector< MSG_PANEL_ITEM > &aList) const
Adds information about the connection object to aList.
static bool IsBusLabel(const wxString &aLabel)
Test if aLabel has a bus notation.
wxString m_suffix
Name suffix (used only for disambiguation)
wxString m_bus_prefix
Optional prefix of a bux group (always empty for nets and vector buses)
void SetSheet(SCH_SHEET_PATH aSheet)
wxString m_cached_name
Full name, including prefix and suffix.
wxString Prefix() const
CONNECTION_GRAPH * m_graph
Pointer to the connection graph for the schematic this connection exists on.
static wxString PrintBusForUI(const wxString &aString)
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...
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
int m_subgraph_code
Groups directly-connected items.
wxString m_vector_prefix
static bool MightBeBusLabel(const wxString &aLabel)
Test if aLabel looks like a bus notation.
int NetCode() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
wxString PathHumanReadable(bool aUseShortRootName=true) const
Return the sheet path in a human readable form made from the sheet names.
#define _(s)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static bool isSuperSubOverbar(wxChar c)
@ BUS
This item represents a bus vector.
@ NET
This item represents a net.
@ NONE
No connection to this item.
@ BUS_GROUP
This item represents a bus group.
wxString UnescapeString(const wxString &aSource)
@ SCH_LABEL_T
Definition: typeinfo.h:150
@ SCH_SHEET_T
Definition: typeinfo.h:157
@ SCH_HIER_LABEL_T
Definition: typeinfo.h:152
@ LIB_PIN_T
Definition: typeinfo.h:202
@ SCH_SHEET_PIN_T
Definition: typeinfo.h:156
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:151
@ SCH_PIN_T
Definition: typeinfo.h:158