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 (C) 2021-2023 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
88 m_parent( nullptr )
89{
90 Reset();
91 Clone( aOther );
92}
93
94
95bool SCH_CONNECTION::operator==( const SCH_CONNECTION& aOther ) const
96{
97 // NOTE: Not comparing m_dirty or net/bus/subgraph codes
98 if( ( aOther.m_driver == m_driver ) &&
99 ( aOther.m_type == m_type ) &&
100 ( aOther.m_name == m_name ) &&
101 ( aOther.m_sheet == m_sheet ) )
102 {
103 return true;
104 }
105
106 return false;
107}
108
109
111{
112 m_driver = aItem;
113
114 recacheName();
115
116 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
117 member->SetDriver( aItem );
118}
119
120
122{
123 m_sheet = aSheet;
124 m_local_sheet = aSheet;
125
126 recacheName();
127
128 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
129 member->SetSheet( aSheet );
130}
131
132
134{
135 return !( aOther == *this );
136}
137
138
139void SCH_CONNECTION::ConfigureFromLabel( const wxString& aLabel )
140{
141 m_members.clear();
142
143 m_name = aLabel;
144 m_local_name = aLabel;
146
147 wxString prefix;
148 std::vector<wxString> members;
149
150 wxString unescaped = UnescapeString( aLabel );
151
152 if( NET_SETTINGS::ParseBusVector( unescaped, &prefix, &members ) )
153 {
154 m_type = CONNECTION_TYPE::BUS;
155 m_vector_prefix = prefix;
156
157 long i = 0;
158
159 for( const wxString& vector_member : members )
160 {
161 auto member = std::make_shared<SCH_CONNECTION>( m_parent, m_sheet );
162 member->m_type = CONNECTION_TYPE::NET;
163 member->m_prefix = m_prefix;
164 member->m_local_name = vector_member;
165 member->m_local_prefix = m_prefix;
166 member->m_vector_index = i++;
167 member->SetName( vector_member );
168 member->SetGraph( m_graph );
169 m_members.push_back( member );
170 }
171 }
172 else if( NET_SETTINGS::ParseBusGroup( unescaped, &prefix, &members ) )
173 {
174 m_type = CONNECTION_TYPE::BUS_GROUP;
175 m_bus_prefix = prefix;
176
177 // Named bus groups generate a net prefix, unnamed ones don't
178 if( !prefix.IsEmpty() )
179 prefix += wxT( "." );
180
181 for( const wxString& group_member : members )
182 {
183 // Handle alias inside bus group member list
184 if( auto alias = m_graph->GetBusAlias( group_member ) )
185 {
186 for( const wxString& alias_member : alias->Members() )
187 {
188 auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
189 member->SetPrefix( prefix );
190 member->SetGraph( m_graph );
191 member->ConfigureFromLabel( EscapeString( alias_member, CTX_NETNAME ) );
192 m_members.push_back( member );
193 }
194 }
195 else
196 {
197 auto member = std::make_shared< SCH_CONNECTION >( m_parent, m_sheet );
198 member->SetPrefix( prefix );
199 member->SetGraph( m_graph );
200 member->ConfigureFromLabel( group_member );
201 m_members.push_back( member );
202 }
203 }
204 }
205 else
206 {
207 m_type = CONNECTION_TYPE::NET;
208 }
209
210 recacheName();
211}
212
213
215{
216 m_type = CONNECTION_TYPE::NONE;
217 m_name.Empty();
218 m_local_name.Empty();
219 m_local_prefix.Empty();
220 m_cached_name.Empty();
222 m_prefix.Empty();
223 m_bus_prefix.Empty();
224 m_suffix .Empty();
226 m_driver = nullptr;
227 m_members.clear();
228 m_dirty = true;
229 m_net_code = 0;
230 m_bus_code = 0;
231 m_subgraph_code = 0;
232 m_vector_start = 0;
233 m_vector_end = 0;
234 m_vector_index = 0;
235 m_vector_prefix.Empty();
236}
237
238
240{
241 m_graph = aOther.m_graph;
242 // Note: m_lastDriver is not cloned as it needs to be the last driver of *this* connection
243 m_driver = aOther.Driver();
244 m_sheet = aOther.Sheet();
245 // Note: m_local_sheet is not cloned
246 m_name = aOther.m_name;
247 m_type = aOther.m_type;
248 // Note: m_local_name is not cloned if not set yet
249 if( m_local_name.IsEmpty() )
250 {
251 m_local_name = aOther.LocalName();
252 m_local_prefix = aOther.Prefix();
253 }
254
255 m_prefix = aOther.Prefix();
256 // m_bus_prefix is not cloned; only used for local names
257 m_suffix = aOther.Suffix();
258 m_net_code = aOther.NetCode();
259 m_bus_code = aOther.BusCode();
260 m_vector_start = aOther.VectorStart();
261 m_vector_end = aOther.VectorEnd();
262 // Note: m_vector_index is not cloned
263 m_vector_prefix = aOther.VectorPrefix();
264
265 // Note: subgraph code isn't cloned, it should remain with the original object
266
267 // Handle vector bus members: make sure local names are preserved where possible
268 const std::vector<std::shared_ptr<SCH_CONNECTION>>& otherMembers = aOther.Members();
269
270 if( m_type == CONNECTION_TYPE::BUS && aOther.Type() == CONNECTION_TYPE::BUS )
271 {
272 if( m_members.empty() )
273 {
274 m_members = otherMembers;
275 }
276 else
277 {
278 size_t cloneLimit = std::min( m_members.size(), otherMembers.size() );
279
280 for( size_t i = 0; i < cloneLimit; ++i )
281 m_members[i]->Clone( *otherMembers[i] );
282 }
283 }
284 else if( m_type == CONNECTION_TYPE::BUS_GROUP && aOther.Type() == CONNECTION_TYPE::BUS_GROUP )
285 {
286 if( m_members.empty() )
287 {
288 m_members = otherMembers;
289 }
290 else
291 {
292 // TODO: refactor this once we support deep nesting
293 for( std::shared_ptr<SCH_CONNECTION>& member : m_members )
294 {
295 auto it = std::find_if( otherMembers.begin(), otherMembers.end(),
296 [&]( const std::shared_ptr<SCH_CONNECTION>& aTest )
297 {
298 return aTest->LocalName() == member->LocalName();
299 } );
300
301 if( it != otherMembers.end() )
302 member->Clone( **it );
303 }
304 }
305 }
306
307 m_type = aOther.Type();
308
309 recacheName();
310}
311
312
314{
315 wxASSERT( Parent() );
316
317 switch( Parent()->Type() )
318 {
319 case SCH_LABEL_T:
321 case SCH_HIER_LABEL_T:
322 case SCH_SHEET_PIN_T:
323 case SCH_SHEET_T:
324 return true;
325
326 case SCH_PIN_T:
327 {
328 const SCH_PIN* pin = static_cast<const SCH_PIN*>( Parent() );
329
330 if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( pin->GetParentSymbol() ) )
331 {
332 // Only annotated symbols should drive nets.
333 return pin->IsGlobalPower() || symbol->IsAnnotated( &m_sheet );
334 }
335
336 return true;
337 }
338
339 default:
340 return false;
341 }
342}
343
344
346{
347 return m_driver != m_lastDriver;
348}
349
350
352{
354}
355
356
357
358wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const
359{
360 wxASSERT( !m_cached_name.IsEmpty() );
361 return aIgnoreSheet ? m_cached_name : m_cached_name_with_path;
362}
363
364
366{
367 wxString retv;
368
369 if( m_graph )
370 {
372
373 if( subgraph )
374 retv = subgraph->GetNetName();
375 }
376
377 return retv;
378}
379
380
382{
383 m_cached_name = m_name.IsEmpty() ? wxString( wxT( "<NO NET>" ) )
384 : wxString( m_prefix ) << m_name << m_suffix;
385
386 bool prepend_path = true;
387
388 if( !Parent() || m_type == CONNECTION_TYPE::NONE )
389 prepend_path = false;
390
391 if( m_driver )
392 {
393 switch( m_driver->Type() )
394 {
396 case SCH_PIN_T:
397 // Pins are either power connections or belong to a uniquely-annotated
398 // symbol, so they don't need a path if they are driving the subgraph.
399 prepend_path = false;
400 break;
401
402 default:
403 break;
404 }
405 }
406
409}
410
411
412void SCH_CONNECTION::SetPrefix( const wxString& aPrefix )
413{
414 m_prefix = aPrefix;
415
416 recacheName();
417
418 for( const std::shared_ptr<SCH_CONNECTION>& m : Members() )
419 m->SetPrefix( aPrefix );
420}
421
422
423void SCH_CONNECTION::SetSuffix( const wxString& aSuffix )
424{
425 m_suffix = aSuffix;
426
427 recacheName();
428
429 for( const std::shared_ptr<SCH_CONNECTION>& m : Members() )
430 m->SetSuffix( aSuffix );
431}
432
433
434void SCH_CONNECTION::AppendInfoToMsgPanel( std::vector<MSG_PANEL_ITEM>& aList ) const
435{
436 wxString msg, group_name, members;
437 std::vector<wxString> group_members;
438
439 aList.emplace_back( _( "Connection Name" ), UnescapeString( Name() ) );
440
441 if( std::shared_ptr<BUS_ALIAS> alias = m_graph->GetBusAlias( m_name ) )
442 {
443 msg.Printf( _( "Bus Alias %s Members" ), m_name );
444 aList.emplace_back( msg, boost::algorithm::join( alias->Members(), " " ) );
445 }
446 else if( NET_SETTINGS::ParseBusGroup( m_name, &group_name, &group_members ) )
447 {
448 for( const wxString& group_member : group_members )
449 {
450 if( std::shared_ptr<BUS_ALIAS> group_alias = m_graph->GetBusAlias( group_member ) )
451 {
452 msg.Printf( _( "Bus Alias %s Members" ), group_alias->GetName() );
453 aList.emplace_back( msg, boost::algorithm::join( group_alias->Members(), " " ) );
454 }
455 }
456 }
457
458#if defined(DEBUG)
459 // These messages are not flagged as translatable, because they are only debug messages
460
461 if( IsBus() )
462 aList.emplace_back( wxT( "Bus Code" ), wxString::Format( "%d", m_bus_code ) );
463
464 aList.emplace_back( wxT( "Subgraph Code" ), wxString::Format( "%d", m_subgraph_code ) );
465
466 if( SCH_ITEM* driver = Driver() )
467 {
468 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MILLIMETRES );
469
470 msg.Printf( wxS( "%s at %p" ), driver->GetItemDescription( &unitsProvider ), driver );
471 aList.emplace_back( wxT( "Connection Source" ), msg );
472 }
473#endif
474}
475
476
477bool SCH_CONNECTION::IsBusLabel( const wxString& aLabel )
478{
479 const wxString& unescaped = UnescapeString( aLabel );
480
481 return NET_SETTINGS::ParseBusVector( unescaped, nullptr, nullptr )
482 || NET_SETTINGS::ParseBusGroup( unescaped, nullptr, nullptr );
483}
484
485
486bool SCH_CONNECTION::MightBeBusLabel( const wxString& aLabel )
487{
488 // Weak heuristic for performance reasons. Stronger test will be used for connectivity
489 wxString label = UnescapeString( aLabel );
490
491 return label.Contains( wxT( "[" ) ) || label.Contains( wxT( "{" ) );
492}
493
494
495const std::vector< std::shared_ptr< SCH_CONNECTION > > SCH_CONNECTION::AllMembers() const
496{
497 std::vector< std::shared_ptr< SCH_CONNECTION > > ret( m_members );
498
499 for( const std::shared_ptr<SCH_CONNECTION>& member : m_members )
500 {
501 if( member->IsBus() )
502 ret.insert( ret.end(), member->Members().begin(), member->Members().end() );
503 }
504
505 return ret;
506}
507
508
509static bool isSuperSubOverbar( wxChar c )
510{
511 return c == '_' || c == '^' || c == '~';
512};
513
514
515wxString SCH_CONNECTION::PrintBusForUI( const wxString& aGroup )
516{
517 size_t groupLen = aGroup.length();
518 size_t i = 0;
519 wxString ret;
520 int braceNesting = 0;
521
522 // Parse prefix
523 //
524 for( ; i < groupLen; ++i )
525 {
526 if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
527 {
528 braceNesting++;
529 i++;
530 continue;
531 }
532 else if( aGroup[i] == '}' )
533 {
534 braceNesting--;
535 continue;
536 }
537
538 ret += aGroup[i];
539
540 if( aGroup[i] == '{' )
541 break;
542 }
543
544 // Parse members
545 //
546 i++; // '{' character
547
548 for( ; i < groupLen; ++i )
549 {
550 if( isSuperSubOverbar( aGroup[i] ) && i + 1 < groupLen && aGroup[i+1] == '{' )
551 {
552 braceNesting++;
553 i++;
554 continue;
555 }
556 else if( aGroup[i] == '}' )
557 {
558 braceNesting--;
559 continue;
560 }
561
562 ret += aGroup[i];
563
564 if( aGroup[i] == '}' )
565 break;
566 }
567
568 return ret;
569}
570
571
573{
574 if( !aOther->IsBus() )
575 return false;
576
577 for( const std::shared_ptr<SCH_CONNECTION>& member : aOther->Members() )
578 {
579 if( member->FullLocalName() == FullLocalName() )
580 return true;
581 }
582
583 return false;
584}
585
586
588{
589 if( !aOther->IsBus() )
590 return false;
591
592 wxString me = Name( true );
593
594 for( const std::shared_ptr<SCH_CONNECTION>& m : aOther->Members() )
595 {
596 if( m->Name( true ) == me )
597 return true;
598 }
599
600 return false;
601}
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
Calculate the connectivity of a schematic and generates netlists.
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem)
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Return a bus alias pointer for the given name if it exists (from cache)
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)
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:100
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
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 bux group (always empty for nets and vector buses)
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
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...
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:174
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, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
Schematic symbol object.
Definition: sch_symbol.h:108
#define _(s)
static bool isSuperSubOverbar(wxChar c)
static bool isSuperSubOverbar(wxChar c)
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
Definition: string_utils.h:53
@ SCH_LABEL_T
Definition: typeinfo.h:167
@ SCH_SHEET_T
Definition: typeinfo.h:174
@ SCH_HIER_LABEL_T
Definition: typeinfo.h:169
@ SCH_SHEET_PIN_T
Definition: typeinfo.h:173
@ SCH_GLOBAL_LABEL_T
Definition: typeinfo.h:168
@ SCH_PIN_T
Definition: typeinfo.h:153