KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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
20 * along with this program. If not, see <https://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
64
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
79 m_parent( nullptr ),
80 m_driver( nullptr ),
81 m_graph( aGraph )
82{
83 Reset();
84}
85
86
88 m_parent( nullptr ),
89 m_driver( nullptr )
90{
91 Reset();
92 Clone( aOther );
93}
94
95
96bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
97{
98 // NOTE: Not comparing m_dirty or net/bus/subgraph codes
99 if( ( aOther.m_driver == m_driver ) &&
100 ( aOther.m_type == m_type ) &&
101 ( aOther.m_name == m_name ) &&
102 ( aOther.m_sheet == m_sheet ) )
103 {
104 return true;
105 }
106
107 return false;
108}
109
110
112{
113 m_driver = aItem;
114
115 recacheName();
116
117 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
118 member->SetDriver( aItem );
119}
120
121
123{
124 m_sheet = aSheet;
125 m_local_sheet = aSheet;
126
127 recacheName();
128
129 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
130 member->SetSheet( aSheet );
131}
132
133
135{
136 return !( aOther == *this );
137}
138
139
140void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
141{
142 m_members.clear();
143
144 m_name = aLabel;
145 m_local_name = aLabel;
147
148 wxString prefix;
149 std::vector<wxString> members;
150
151 wxString unescaped = UnescapeString( aLabel );
152
153 if( NET_SETTINGS::ParseBusVector( unescaped, &prefix, &members ) )
154 {
156 m_vector_prefix = std::move( prefix );
157
158 long i = 0;
159
160 for( const wxString& vector_member : members )
161 {
162 std::shared_ptr<SCH_CONNECTION> member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
163 wxString escapedMember = EscapeString( vector_member, CTX_NETNAME );
164
165 member->m_type = CONNECTION_TYPE::NET;
166 member->m_prefix = m_prefix;
167 member->m_local_name = escapedMember;
168 member->m_local_prefix = m_prefix;
169 member->m_vector_index = i++;
170 member->SetName( escapedMember );
171 member->SetGraph( m_graph );
172 m_members.push_back( std::move( member ) );
173 }
174 }
175 else if( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
176 {
178 m_bus_prefix = prefix;
179
180 // Named bus groups generate a net prefix, unnamed ones don't
181 if( !prefix.IsEmpty() )
182 prefix += wxT( "." );
183
184 for( const wxString& group_member : members )
185 {
186 // Handle alias inside bus group member list
187 if( auto alias = m_graph->GetBusAlias( group_member ) )
188 {
189 for( const wxString& alias_member : alias->Members() )
190 {
191 std::shared_ptr<SCH_CONNECTION> member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
192 member->SetPrefix( prefix );
193 member->SetGraph( m_graph );
194 member->ConfigureFromLabel( EscapeString( alias_member, CTX_NETNAME ) );
195 m_members.push_back( std::move( member ) );
196 }
197 }
198 else
199 {
200 std::shared_ptr<SCH_CONNECTION> member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
201 member->SetPrefix( prefix );
202 member->SetGraph( m_graph );
203 member->ConfigureFromLabel( EscapeString( group_member, CTX_NETNAME ) );
204 m_members.push_back( std::move( member ) );
205 }
206 }
207 }
208 else
209 {
211 }
212
213 recacheName();
214}
215
216
218{
220 m_name.Empty();
221 m_local_name.Empty();
222 m_local_prefix.Empty();
223 m_cached_name.Empty();
225 m_prefix.Empty();
226 m_bus_prefix.Empty();
227 m_suffix .Empty();
229 m_driver = nullptr;
230 m_members.clear();
231 m_dirty = true;
232 m_net_code = 0;
233 m_bus_code = 0;
234 m_subgraph_code = 0;
235 m_vector_start = 0;
236 m_vector_end = 0;
237 m_vector_index = 0;
238 m_vector_prefix.Empty();
239}
240
241
243{
244 m_graph = aOther.m_graph;
245
246 // Note: m_lastDriver is not cloned as it needs to be the last driver of *this* connection
247 m_driver = aOther.Driver();
248 m_sheet = aOther.Sheet();
249
250 // Note: m_local_sheet is not cloned
251 m_name = aOther.m_name;
252
253 CONNECTION_TYPE origType = m_type;
254 m_type = aOther.m_type;
255
256 // Note: m_local_name is not cloned if not set yet
257 if( m_local_name.IsEmpty() )
258 {
259 m_local_name = aOther.LocalName();
260 m_local_prefix = aOther.Prefix();
261 }
262
263 m_prefix = aOther.Prefix();
264
265 // m_bus_prefix is not cloned; only used for local names
266 m_suffix = aOther.Suffix();
267 m_net_code = aOther.NetCode();
268 m_bus_code = aOther.BusCode();
269 m_vector_start = aOther.VectorStart();
270 m_vector_end = aOther.VectorEnd();
271
272 // Note: m_vector_index is not cloned
273 m_vector_prefix = aOther.VectorPrefix();
274
275 // Note: subgraph code isn't cloned, it should remain with the original object
276
277 // Handle vector bus members: make sure local names are preserved where possible
278 const std::vector<std::shared_ptr<SCH_CONNECTION>>& otherMembers = aOther.Members();
279
280 auto cloneMember =
281 [&]( const SCH_CONNECTION& aSrc ) -> std::shared_ptr<SCH_CONNECTION>
282 {
283 auto copy = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
284 copy->SetGraph( m_graph );
285 copy->Clone( aSrc );
286
287 copy->m_vector_index = aSrc.m_vector_index;
288
289 return copy;
290 };
291
292 if( origType == CONNECTION_TYPE::BUS && aOther.Type() == CONNECTION_TYPE::BUS )
293 {
294 if( m_members.empty() )
295 {
296 m_members.reserve( otherMembers.size() );
297
298 for( const std::shared_ptr<SCH_CONNECTION>& src : otherMembers )
299 m_members.push_back( cloneMember( *src ) );
300 }
301 else
302 {
303 size_t cloneLimit = std::min( m_members.size(), otherMembers.size() );
304
305 for( size_t i = 0; i < cloneLimit; ++i )
306 m_members[i]->Clone( *otherMembers[i] );
307 }
308 }
309 else if( origType == CONNECTION_TYPE::BUS_GROUP && aOther.Type() == CONNECTION_TYPE::BUS_GROUP )
310 {
311 if( m_members.empty() )
312 {
313 m_members.reserve( otherMembers.size() );
314
315 for( const std::shared_ptr<SCH_CONNECTION>& src : otherMembers )
316 m_members.push_back( cloneMember( *src ) );
317 }
318 else
319 {
320 // TODO: refactor this once we support deep nesting
321 for( std::shared_ptr<SCH_CONNECTION>& member : m_members )
322 {
323 auto it = std::find_if( otherMembers.begin(), otherMembers.end(),
324 [&]( const std::shared_ptr<SCH_CONNECTION>& aTest )
325 {
326 return aTest->LocalName() == member->LocalName();
327 } );
328
329 if( it != otherMembers.end() )
330 member->Clone( **it );
331 }
332 }
333 }
334 else if( aOther.IsBus() )
335 {
336 m_members.clear();
337 m_members.reserve( otherMembers.size() );
338
339 for( const std::shared_ptr<SCH_CONNECTION>& src : otherMembers )
340 m_members.push_back( cloneMember( *src ) );
341 }
342
343 m_type = aOther.Type();
344
345 recacheName();
346}
347
348
350{
351 wxASSERT( Parent() );
352
353 switch( Parent()->Type() )
354 {
355 case SCH_LABEL_T:
357 case SCH_HIER_LABEL_T:
358 case SCH_SHEET_PIN_T:
359 case SCH_SHEET_T:
360 case SCH_PIN_T: return true;
361
362 default:
363 return false;
364 }
365}
366
367
369{
370 return m_driver != m_lastDriver;
371}
372
373
378
379
380
381wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
382{
383 wxASSERT( !m_cached_name.IsEmpty() );
384 return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
385}
386
387
389{
390 wxString retv;
391
392 if( m_graph )
393 {
394 CONNECTION_SUBGRAPH* subgraph = m_graph->GetSubgraphForItem( m_parent );
395
396 if( subgraph )
397 retv = subgraph->GetNetName();
398 }
399
400 return retv;
401}
402
403
405{
406 m_cached_name = m_name.IsEmpty() ? wxString( wxT( "<NO NET>" ) )
407 : wxString( m_prefix ) << m_name << m_suffix;
408
409 bool prepend_path = true;
410
411 if( !Parent() || m_type == CONNECTION_TYPE::NONE )
412 prepend_path = false;
413
414 if( m_driver )
415 {
416 switch( m_driver->Type() )
417 {
419 prepend_path = false;
420 break;
421
422 case SCH_PIN_T:
423 { // Normal pins and global power pins do not need a path. But local power pins do
424 SCH_PIN* pin = static_cast<SCH_PIN*>( m_driver );
425
426 prepend_path = pin->IsLocalPower();
427 break;
428 }
429
430 default:
431 break;
432 }
433 }
434
435 // Use aEscapeSheetNames=true so that sheets with '/' in their names have the slash
436 // escaped to "{slash}". This ensures pattern matching for net classes works correctly
437 // since '/' is used as the hierarchy separator.
438 m_cached_name_with_path = prepend_path
439 ? m_sheet.PathHumanReadable( true, false, true ) << m_cached_name
441}
442
443
444void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
445{
446 m_prefix = aPrefix;
447
448 recacheName();
449
450 for( const std::shared_ptr<SCH_CONNECTION>& m : Members() )
451 m->SetPrefix( aPrefix );
452}
453
454
455void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
456{
457 m_suffix = aSuffix;
458
459 recacheName();
460}
461
462
463void SCH_CONNECTION::AppendInfoToMsgPanel( std::vector<MSG_PANEL_ITEM>& aList ) const
464{
465 wxString msg, group_name;
466 std::vector<wxString> group_members;
467
468 aList.emplace_back( _( "Connection Name" ), UnescapeString( Name() ) );
469
470 if( IsBus() )
471 {
472 if( std::shared_ptr<BUS_ALIAS> alias = m_graph->GetBusAlias( m_name ) )
473 {
474 msg.Printf( _( "Bus Alias %s Members" ), m_name );
475 aList.emplace_back( msg, boost::algorithm::join( alias->Members(), " " ) );
476 }
477 else if( NET_SETTINGS::ParseBusGroup( m_name, &group_name, &group_members ) )
478 {
479 for( const wxString& group_member : group_members )
480 {
481 if( std::shared_ptr<BUS_ALIAS> group_alias = m_graph->GetBusAlias( group_member ) )
482 {
483 msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
484 aList.emplace_back( msg, boost::algorithm::join( group_alias->Members(), " " ) );
485 }
486 }
487 }
488 }
489
490#if defined(DEBUG)
491 // These messages are not flagged as translatable, because they are only debug messages
492
493 if( IsBus() )
494 aList.emplace_back( wxT( "Bus Code" ), wxString::Format( "%d", m_bus_code ) );
495
496 aList.emplace_back( wxT( "Subgraph Code" ), wxString::Format( "%d", m_subgraph_code ) );
497
498 if( SCH_ITEM* driver = Driver() )
499 {
500 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
501
502 msg.Printf( wxS( "%s at %p" ),
503 driver->GetItemDescription( &unitsProvider, false ),
504 driver );
505 aList.emplace_back( wxT( "Connection Source" ), msg );
506 }
507#endif
508}
509
510
511bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
512{
513 const wxString& unescaped = UnescapeString( aLabel );
514
515 return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
516 || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
517}
518
519
520bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
521{
522 // Weak heuristic for performance reasons. Stronger test will be used for connectivity
523 wxString label = UnescapeString( aLabel );
524
525 return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
526}
527
528
529const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
530{
531 std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
532
533 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
534 {
535 if( member->IsBus() )
536 ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
537 }
538
539 return ret;
540}
541
542
543static bool isSuperSubOverbar( wxChar c )
544{
545 return c == '_' || c == '^' || c == '~';
546};
547
548
549wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
550{
551 // Net names are stored escaped (e.g. '/' is encoded as "{slash}") so that they round-trip
552 // through the s-expression parser. The unfold-from-bus menu and similar UI surfaces want
553 // the human-readable form, so unescape before walking the formatting markup. Otherwise
554 // the "{slash}" sequence is mis-parsed as an opening overbar/sub/super group and the
555 // trailing '}' is silently dropped.
556 wxString unescaped = UnescapeString( aGroup );
557
558 size_t groupLen = unescaped.length();
559 size_t i = 0;
560 wxString ret;
561
562 // Parse prefix
563 //
564 for( ; i < groupLen; ++i )
565 {
566 if( isSuperSubOverbar( unescaped[i] ) && i + 1 < groupLen && unescaped[i+1] == '{' )
567 {
568 ret += unescaped[i];
569 i++;
570 continue;
571 }
572 else if( unescaped[i] == '}' )
573 {
574 continue;
575 }
576
577 // Handle backslash-escaped spaces (display without the backslash)
578 if( unescaped[i] == '\\' && i + 1 < groupLen && unescaped[i + 1] == ' ' )
579 {
580 ret += ' ';
581 i++;
582 continue;
583 }
584
585 ret += unescaped[i];
586
587 if( unescaped[i] == '{' )
588 break;
589 }
590
591 // Parse members
592 //
593 i++; // '{' character
594
595 for( ; i < groupLen; ++i )
596 {
597 if( isSuperSubOverbar( unescaped[i] ) && i + 1 < groupLen && unescaped[i+1] == '{' )
598 {
599 ret += unescaped[i];
600 i++;
601 continue;
602 }
603 else if( unescaped[i] == '}' )
604 {
605 continue;
606 }
607
608 // Handle backslash-escaped spaces (display without the backslash)
609 if( unescaped[i] == '\\' && i + 1 < groupLen && unescaped[i + 1] == ' ' )
610 {
611 ret += ' ';
612 i++;
613 continue;
614 }
615
616 ret += unescaped[i];
617
618 if( unescaped[i] == '}' )
619 break;
620 }
621
622 return ret;
623}
624
625
627{
628 if( !aOther->IsBus() )
629 return false;
630
631 if( !IsBus() )
632 {
633 for( const std::shared_ptr<SCH_CONNECTION>& otherMember : aOther->Members() )
634 {
635 if( FullLocalName() == otherMember->FullLocalName() )
636 return true;
637 }
638
639 return false;
640 }
641
642 // If both connections are buses, check if all members of this bus are in the other bus
643 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
644 {
645 bool found = false;
646
647 for( const std::shared_ptr<SCH_CONNECTION>& otherMember : aOther->Members() )
648 {
649 if( member->FullLocalName() == otherMember->FullLocalName() )
650 {
651 found = true;
652 break;
653 }
654 }
655
656 // If one of the members is not found in the other connection, this is not a subset
657 if( !found )
658 return false;
659 }
660
661 return true;
662}
663
664
666{
667 if( !aOther->IsBus() )
668 return false;
669
670 wxString me = Name( true );
671
672 for( const std::shared_ptr<SCH_CONNECTION>& m : aOther->Members() )
673 {
674 if( m->Name( true ) == me )
675 return true;
676 }
677
678 return false;
679}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
Calculate the connectivity of a schematic and generate netlists.
A subgraph is a set of items that are electrically connected on a single sheet.
wxString GetNetName() const
Return the fully-qualified net name for this subgraph (if one exists)
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList, size_t *aPrefixEnd=nullptr)
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.
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)
Unlike SetPrefix, the suffix is not pushed onto bus members.
long VectorStart() const
wxString m_name
Name of the connection.
SCH_ITEM * Parent() const
wxString GetNetName() 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
SCH_CONNECTION(SCH_ITEM *aParent=nullptr, const SCH_SHEET_PATH &aPath=SCH_SHEET_PATH())
Buses can be defined in multiple ways.
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...
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 bus group (always empty for nets and vector buses)
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
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...
void SetSheet(const SCH_SHEET_PATH &aSheet)
int m_subgraph_code
Groups directly-connected items.
wxString m_vector_prefix
Prefix name of the vector, if m_type == CONNECTION_BUS (or "" if not).
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:165
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
#define _(s)
static bool isSuperSubOverbar(wxChar c)
static bool isSuperSubOverbar(wxChar c)
CONNECTION_TYPE
@ 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)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
KIBIS_PIN * pin
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_PIN_T
Definition typeinfo.h:149