KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_facade.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "conn_facade.h"
21#include "conn_netchain_input.h"
23#include "conn_text.h"
24#include <algorithm>
25#include <array>
26#include <iterator>
27#include <ranges>
28#include <span>
29#include <limits>
30#include <stdexcept>
31#include <utility>
32#include <schematic.h>
33#include <bus_alias.h>
34#include <sch_screen.h>
35#include <sch_sheet_path.h>
36#include <sch_sheet.h>
37#include <sch_line.h>
38#include <sch_bus_entry.h>
39#include <sch_label.h>
40#include <sch_pin.h>
41#include <sch_rule_area.h>
44#include <wx/filefn.h>
46#include <wx/thread.h>
48
49namespace SCH_CONNECTIVITY
50{
51namespace
52{
53std::optional<TEXT_EVAL_VCS::CONTEXT_PATH_SCOPE> sourceProjectContext( const SCHEMATIC& aSchematic )
54{
55 if( aSchematic.IsValid() )
56 {
57 if( const wxString path = aSchematic.Project().GetProjectPath(); !path.IsEmpty() )
58 return std::optional<TEXT_EVAL_VCS::CONTEXT_PATH_SCOPE>( std::in_place, path );
59 }
60
61 return std::nullopt;
62}
63
64struct INSTANCE_TEXT_CONTEXT
65{
66 KIID_PATH path;
67 SCREEN_ID screen = 0;
68 wxString page;
69 wxString humanPath;
70 wxString sheetName;
71 wxString filename;
72 std::vector<wxString> title;
73 wxString paper;
74 VECTOR2D pageSize;
75 bool portrait = false;
76 bool operator==( const INSTANCE_TEXT_CONTEXT& ) const = default;
77};
78
79struct VARIANT_TEXT_CONTEXT
80{
81 std::map<wxString, wxString> fields;
82 std::array<bool, 5> attributes{};
83 bool operator==( const VARIANT_TEXT_CONTEXT& ) const = default;
84};
85
86struct SHEET_TEXT_CONTEXT
87{
88 std::vector<std::tuple<FIELD_T, wxString, wxString>> fields;
89 std::array<bool, 5> attributes{};
90 std::map<KIID_PATH, std::map<wxString, VARIANT_TEXT_CONTEXT>> variants;
91 bool operator==( const SHEET_TEXT_CONTEXT& ) const = default;
92};
93
94struct MODEL_TEXT_CONTEXT
95{
96 wxString projectFile;
97 wxString projectName;
98 wxString rootFile;
99 wxString workingDirectory;
100 wxString drawingLayout;
101 bool allowEmptyLayout = false;
102 wxString vcsContext;
103 bool vcsContextIsFile = false;
104 wxString variant;
105 wxString variantDescription;
106 int sheetCount = 0;
107 std::map<wxString, wxString> variables;
108 std::vector<wxString> templateFields;
109 std::vector<INSTANCE_TEXT_CONTEXT> instances;
110 std::map<KIID_PATH, SHEET_TEXT_CONTEXT> sheets;
111
112 // A package's suffix comes from units on other screens, so a rename there must bump the epoch
113 std::map<std::pair<KIID, KIID_PATH>, wxString> packageSuffixes;
114 bool operator==( const MODEL_TEXT_CONTEXT& ) const = default;
115};
116
117std::optional<MODEL_TEXT_CONTEXT> CaptureTextContext( SCHEMATIC& aSchematic, const SCH_SHEET_LIST& aPaths )
118{
119 MODEL_TEXT_CONTEXT result;
120
121 if( !aSchematic.IsValid() )
122 return std::nullopt;
123
124 PROJECT& project = aSchematic.Project();
125 result.projectFile = project.GetProjectFullName();
126 result.projectName = project.GetProjectName();
127 result.rootFile = aSchematic.GetFileName();
128 result.workingDirectory = wxGetCwd();
129
130 if( project.GetProjectPath().empty() )
131 {
133 result.vcsContextIsFile = TEXT_EVAL_VCS::GetContextIsFile();
134 }
135
136 DS_DATA_MODEL& layout = DS_DATA_MODEL::GetTheInstance();
137 layout.SaveInString( &result.drawingLayout );
138 result.allowEmptyLayout = layout.VoidListAllowed();
139 result.variant = aSchematic.GetCurrentVariant();
140 result.variantDescription = aSchematic.GetVariantDescription( result.variant );
141 result.sheetCount = aSchematic.Root().CountSheets();
142 result.variables = project.GetTextVars();
143 result.packageSuffixes = aSchematic.PackageRefSuffixes();
144
145 for( const auto& field : project.GetProjectFile().m_TemplateFieldNames.GetResolvedTemplateFieldNames() )
146 result.templateFields.push_back( field.m_Name );
147
148 for( const SCH_SHEET_PATH& path : aPaths )
149 {
150 SCH_SCREEN* screen = path.LastScreen();
151
152 if( !screen )
153 continue;
154
155 const TITLE_BLOCK& title = screen->GetTitleBlock();
156 INSTANCE_TEXT_CONTEXT instance{ path.Path(), screen->ConnectivityId(),
157 path.GetPageNumber(), path.PathHumanReadable(), path.Last()->GetName(),
158 screen->GetFileName(),
159 { title.GetTitle(), title.GetDate(), title.GetRevision(), title.GetCompany() },
161 screen->GetPageSettings().GetSizeMils(),
162 screen->GetPageSettings().IsPortrait() };
163
164 for( int comment = 0; comment < 9; ++comment )
165 instance.title.push_back( title.GetComment( comment ) );
166
167 result.instances.push_back( std::move( instance ) );
168 KIID_PATH sheetPath;
169
170 for( size_t depth = 0; depth < path.size(); ++depth )
171 {
172 const SCH_SHEET* sheet = path.at( depth );
173 sheetPath.push_back( sheet->m_Uuid );
174
175 if( result.sheets.contains( sheetPath ) )
176 continue;
177
178 SHEET_TEXT_CONTEXT source;
179 source.attributes = { sheet->GetDNP(), sheet->GetExcludedFromSim(), sheet->GetExcludedFromBOM(),
181
182 for( const SCH_FIELD& field : sheet->GetFields() )
183 source.fields.emplace_back( field.GetId(), field.GetName( false ), field.GetText() );
184
185 for( const SCH_SHEET_INSTANCE& sheetInstance : sheet->GetInstances() )
186 {
187 auto& variants = source.variants[sheetInstance.m_Path];
188
189 for( const auto& [name, variant] : sheetInstance.m_Variants )
190 {
191 variants.emplace( name, VARIANT_TEXT_CONTEXT{
192 variant.m_Fields,
193 { variant.m_DNP, variant.m_ExcludedFromSim, variant.m_ExcludedFromBOM,
194 variant.m_ExcludedFromBoard, variant.m_ExcludedFromPosFiles } } );
195 }
196 }
197
198 result.sheets.emplace( sheetPath, std::move( source ) );
199 }
200 }
201
202 std::ranges::sort( result.instances, {}, &INSTANCE_TEXT_CONTEXT::path );
203 return result;
204}
205} // namespace
206
208{
210 {
211 std::shared_ptr<const COMPONENT_CONTENT> content;
212 std::map<INST_ID, std::vector<KIID>> instances;
213 };
214
216 {
217 std::vector<TEXT_CHECK_FACT> items;
218 std::optional<std::vector<TEXT_CHECK_FACT>> drawingSheet;
219 };
220
222 uint64_t resetGeneration = 0;
223 uint64_t dispatchGeneration = 0;
224 uint64_t nextListener = 0;
225 std::map<uint64_t, std::function<void( const CHANGE_SET& )>> listeners;
226 uint64_t textEpoch = 0;
227 std::optional<MODEL_TEXT_CONTEXT> textContext;
228 bool publicationApplied = false;
229 std::optional<std::map<KIID_PATH, TEXT_CHECKS>> textChecks;
230 std::optional<std::map<KIID_PATH, std::vector<SIMULATION_MODEL_FACT>>> simulationModels;
231 std::map<INST_ID, std::weak_ptr<const SCH_SCREEN::CONNECTIVITY_SOURCE>> sources;
232 std::map<NODE_ID, std::shared_ptr<const NET_ITEMS>> netItems;
233 unsigned publicationHolds = 0;
234
236 {
238 throw std::logic_error( "ERC sources require applied connectivity outside input capture" );
239
240 auto paths = aSchematic.BuildSheetListSortedByPageNumbers();
241
242 for( const SCH_SHEET_PATH& path : paths )
243 {
244 if( !path.LastScreen() )
245 continue;
246
247 const auto instance = engine.Keys().FindInstance( path.PathRef() );
248
249 if( !instance || Screen( *instance ) != path.LastScreen() )
250 throw std::logic_error( "ERC sources require current connectivity" );
251 }
252
253 return paths;
254 }
255
257 {
258 decltype( netItems ) current;
259
260 for( const auto& [node, component] : engine.Published().Components() )
261 {
262 const auto old = netItems.find( node );
263
264 if( old != netItems.end() && old->second->content == component.content )
265 {
266 current.emplace( node, old->second );
267 continue;
268 }
269
270 auto membership = std::make_shared<NET_ITEMS>();
271 membership->content = component.content;
272
273 for( const ITEM_KEY& item : component.content->items )
274 membership->instances[item.inst].push_back( item.item );
275
276 for( const SLOT_KEY& slot : component.content->slots )
277 membership->instances.try_emplace( slot.bundleDriver.inst );
278
279 current.emplace( node, std::move( membership ) );
280 }
281
282 netItems.swap( current );
283 }
284
285 std::vector<INST_ID> LiveInstances() const
286 {
287 std::vector<INST_ID> live;
288 live.reserve( sources.size() );
289
290 for( const auto& [instance, source] : sources )
291 {
292 if( Screen( source ) )
293 live.push_back( instance );
294 }
295
296 return live;
297 }
298
299 bool AnyLive( NODE_ID aNode, std::span<const INST_ID> aLive ) const
300 {
301 const auto found = netItems.find( aNode );
302 return found != netItems.end() && std::any_of( found->second->instances.begin(),
303 found->second->instances.end(), [&]( const auto& entry )
304 {
305 return std::binary_search( aLive.begin(), aLive.end(), entry.first );
306 } );
307 }
308
309 std::optional<NODE_ID> BusNode( const wxString& aName ) const
310 {
311 const auto node = engine.Published().FindByName( aName );
312 return node && engine.Published().Components().at( *node ).content->kind == KIND::BUNDLE
313 ? node : std::nullopt;
314 }
315
316 std::optional<NET_GROUP> Group( NODE_ID aNode, std::span<const INST_ID> aLive,
317 const std::weak_ptr<const FACADE_STATE>& aState ) const
318 {
319 const auto component = engine.Published().Components().find( aNode );
320
321 if( component == engine.Published().Components().end() || component->second.name == INVALID_ID )
322 return std::nullopt;
323
324 std::vector<INST_ID> instances;
325
326 for( const auto& [instance, items] : netItems.at( aNode )->instances )
327 {
328 if( std::binary_search( aLive.begin(), aLive.end(), instance ) )
329 instances.push_back( instance );
330 }
331
332 if( instances.empty() )
333 return std::nullopt;
334
335 std::ranges::sort( instances, std::less<>{}, std::bind_front( &SESSION_KEYS::Instance, &engine.Keys() ) );
336 NET_GROUP result{ engine.Keys().Name( component->second.name ), {} };
337 result.instances.reserve( instances.size() );
338
339 for( INST_ID instance : instances )
340 result.instances.push_back( NET_VIEW( aState, aNode, instance ) );
341
342 return result;
343 }
344
345 std::vector<NET_GROUP> Groups( std::vector<NODE_ID> aNodes, std::span<const INST_ID> aLive,
346 const std::weak_ptr<const FACADE_STATE>& aState ) const
347 {
348 // Unnamed components have no name to sort by, and Group drops them anyway
349 std::erase_if( aNodes,
350 [&]( NODE_ID aNode )
351 {
352 return engine.Published().Components().at( aNode ).name == INVALID_ID;
353 } );
354 std::ranges::sort( aNodes, NAME_LESS{ &engine.Keys() },
355 [&]( NODE_ID aNode )
356 {
357 return engine.Published().Components().at( aNode ).name;
358 } );
359 std::vector<NET_GROUP> result;
360 result.reserve( aNodes.size() );
361
362 for( NODE_ID node : aNodes )
363 {
364 if( auto group = Group( node, aLive, aState ) )
365 result.push_back( std::move( *group ) );
366 }
367
368 return result;
369 }
370
371 const PUBLISHED_COMPONENT* Net( NODE_ID aNode, INST_ID aInstance ) const
372 {
373 wxASSERT( wxThread::IsMain() );
374 const auto found = netItems.find( aNode );
375
376 if( found == netItems.end() || !found->second->instances.contains( aInstance ) || !Screen( aInstance ) )
377 return nullptr;
378
379 return &engine.Published().Components().at( aNode );
380 }
381
382 wxString Name( const PUBLISHED_COMPONENT& aComponent, bool aIgnoreSheet ) const
383 {
384 if( aComponent.name == INVALID_ID )
385 return {};
386
387 const wxString& name = engine.Keys().Name( aComponent.name );
388
389 if( aIgnoreSheet && aComponent.content->best )
390 {
391 const NAME_ID path = aComponent.content->best->path;
392
393 if( path != INVALID_ID )
394 return name.Mid( engine.Keys().Name( path ).length() );
395 }
396
397 return name;
398 }
399
400 KIID_PATH Sheet( const std::optional<ITEM_KEY>& aDriver ) const
401 {
402 return aDriver && Resolve( *aDriver ) ? engine.Keys().Instance( aDriver->inst ) : KIID_PATH();
403 }
404
406 const std::weak_ptr<const FACADE_STATE>& aState, const ITEM_RESULT* aRow = nullptr ) const
407 {
408 const auto& component = engine.Published().Components().at( aNode );
409 const auto& content = *component.content;
410
411 if( content.kind != KIND::BUNDLE || !content.best || !Resolve( content.best->source ) )
412 return {};
413
415 const auto* local = aRow && aRow->busSource ? &*aRow->busSource : nullptr;
416
417 if( local && !Resolve( local->source ) )
418 return {};
419
420 result.schema = local ? local->schema : content.best->schema;
421 result.tree = engine.FindBusTree( engine.Keys().Name( local ? aRow->localName : content.best->name ) );
422 wxASSERT( result.tree && content.best->schema->leaves.size() == content.members.size() );
423 std::span<const SLOT_KEY> slots = content.members;
424 std::vector<SLOT_KEY> localSlots;
425
426 if( local )
427 {
428 localSlots.resize( result.schema->leaves.size() );
429 // Keep local lookup aligned with BindBundle's matching and extra-slot identities
430 const BUS_ALIGNMENT alignment = Align( *result.schema, *content.best->schema );
431
432 for( const auto& [from, to] : alignment.matched )
433 localSlots[from] = content.members.at( to );
434
435 for( size_t from : alignment.unmappedLeft )
436 localSlots[from] = SLOT_KEY{ local->source, static_cast<uint32_t>( from ) };
437
438 slots = localSlots;
439 }
440
441 result.leaves.reserve( slots.size() );
442
443 for( const SLOT_KEY& slot : slots )
444 {
445 const NODE_ID leaf = engine.Published().SlotComponents().at( slot );
446 const auto& instances = netItems.at( leaf )->instances;
447 const INST_ID instance = instances.contains( aInstance ) ? aInstance : slot.bundleDriver.inst;
448 result.leaves.push_back( NET_VIEW( aState, leaf, instance ) );
449 }
450
451 return result;
452 }
453
454 SCH_SCREEN* Screen( INST_ID aInstance ) const
455 {
456 const auto found = sources.find( aInstance );
457
458 if( found == sources.end() )
459 return nullptr;
460
461 return Screen( found->second );
462 }
463
464 SCH_SCREEN* Screen( const std::weak_ptr<const SCH_SCREEN::CONNECTIVITY_SOURCE>& aSource ) const
465 {
466 const auto source = aSource.lock();
467
468 if( !source || !source->screen )
469 return nullptr;
470
471 SCH_SCREEN* screen = source->screen;
472 const auto& revisions = engine.Published().Auxiliary().ScreenRevisions();
473 const auto revision = revisions.find( screen->ConnectivityId() );
474
475 if( revision == revisions.end() )
476 return nullptr;
477
478 if( revision->second == screen->ConnectivityRevision() || publicationHolds > 0 )
479 return screen;
480
481 return RENDER_SCOPE::Active() ? screen : nullptr;
482 }
483
484 bool Current( const SCH_SCREEN& aScreen ) const
485 {
486 const auto& revisions = engine.Published().Auxiliary().ScreenRevisions();
487 const auto revision = revisions.find( aScreen.ConnectivityId() );
488 return revision != revisions.end() && revision->second == aScreen.ConnectivityRevision();
489 }
490
491 const ITEM_RESULT* Row( const ITEM_KEY& aItem ) const
492 {
493 wxASSERT( wxThread::IsMain() );
494
495 const SCH_SCREEN* screen = Screen( aItem.inst );
496
497 if( !screen )
498 return nullptr;
499
500 const auto& rows = engine.Published().Rows();
501 const auto row = rows.find( aItem );
502
503 if( row == rows.end() )
504 return nullptr;
505
506 // A canvas read of a changed screen must not revive the row of a deleted item
507 if( publicationHolds == 0 && !Current( *screen ) && !screen->GetConnectivityItem( aItem.item ) )
508 return nullptr;
509
510 return &row->second;
511 }
512
513 SCH_ITEM* Resolve( const ITEM_KEY& aItem ) const
514 {
515 SCH_SCREEN* screen = Screen( aItem.inst );
516 return screen ? screen->GetConnectivityItem( aItem.item ) : nullptr;
517 }
518};
519
520SUBSCRIPTION::SUBSCRIPTION( std::weak_ptr<FACADE_STATE> aState, uint64_t aId ) :
521 m_state( std::move( aState ) ), m_id( aId )
522{
523}
524
526
528 m_state( std::move( aOther.m_state ) ), m_id( std::exchange( aOther.m_id, 0 ) )
529{
530}
531
533{
534 if( this != &aOther )
535 {
536 Reset();
537 m_state = std::move( aOther.m_state );
538 m_id = std::exchange( aOther.m_id, 0 );
539 }
540
541 return *this;
542}
543
545{
546 wxASSERT( wxThread::IsMain() );
547
548 if( auto state = m_state.lock() )
549 state->listeners.erase( m_id );
550
551 m_state.reset();
552 m_id = 0;
553}
554
555FACADE::FACADE() : m_state( std::make_shared<FACADE_STATE>() ) {}
556
558{
559 Clear();
560 m_state->listeners.clear();
561}
562
563SUBSCRIPTION FACADE::Subscribe( std::function<void( const CHANGE_SET& )> aListener )
564{
565 wxASSERT( wxThread::IsMain() );
566
567 if( !aListener )
568 throw std::invalid_argument( "Connectivity listener is empty" );
569
570 if( m_state->nextListener == std::numeric_limits<uint64_t>::max() )
571 throw std::overflow_error( "Connectivity listener identities exhausted" );
572
573 const uint64_t id = ++m_state->nextListener;
574 m_state->listeners.emplace( id, std::move( aListener ) );
575 return SUBSCRIPTION( m_state, id );
576}
577
578void FACADE::Recalculate( SCHEMATIC& aSchematic, bool aRebuild,
579 const std::function<void( SCH_ITEM* )>& aChangedHandler )
580{
581 wxASSERT( wxThread::IsMain() );
582
583 if( !aSchematic.IsValid() )
584 throw std::invalid_argument( "Cannot apply connectivity without a schematic project" );
585
586 const auto state = m_state;
587 const auto handler = aChangedHandler;
588 const auto paths = aSchematic.BuildSheetListSortedByPageNumbers();
589
590 try
591 {
592 updateModel( aSchematic, paths, aRebuild );
593
594 NETCHAIN_INPUT chains;
595 chains.sheets.reserve( paths.size() );
596 struct SHEET_SOURCE
597 {
599 SCH_SCREEN* screen;
600 };
601 std::map<INST_ID, SHEET_SOURCE> sheets;
602
603 for( const SCH_SHEET_PATH& path : paths )
604 {
605 if( !path.LastScreen() )
606 continue;
607
608 const auto instance = Keys().FindInstance( path.PathRef() );
609
610 if( !instance )
611 throw std::logic_error( "Missing sheet instance while rebuilding netchains" );
612
613 SCH_SCREEN* screen = state->Screen( *instance );
614
615 if( !screen )
616 throw std::logic_error( "Stale screen while rebuilding netchains" );
617
618 chains.sheets.emplace_back( path, &chains.storage );
619 sheets.emplace( *instance, SHEET_SOURCE{ &chains.sheets.back(), screen } );
620 }
621
622 for( const auto& [key, row] : Published().Rows() )
623 {
624 const auto sheet = sheets.find( key.inst );
625 SCH_ITEM* item = sheet != sheets.end()
626 ? sheet->second.screen->GetConnectivityItem( key.item ) : nullptr;
627
628 if( !item )
629 throw std::logic_error( "Stale item while rebuilding netchains" );
630
631 if( row.kind != KIND::SIGNAL || ( item->Type() != SCH_PIN_T && item->Type() != SCH_LABEL_T ) )
632 continue;
633
634 NETCHAIN_INPUT::NET net{ row.name == INVALID_ID ? wxString() : Keys().Name( row.name ), {} };
635 net.key = SCH_NETCHAIN::MakeKey( net.name, row.component );
636 sheet->second.input->nets.emplace( item, std::move( net ) );
637 }
638
639 aSchematic.NetChains().Rebuild( chains );
640 }
641 catch( ... )
642 {
643 Clear();
644 throw;
645 }
646
647 std::map<SCREEN_ID, INST_ID> displayed;
648
649 for( const SCH_SHEET_PATH& path : paths )
650 {
651 if( const auto instance = Keys().FindInstance( path.PathRef() ) )
652 {
653 if( SCH_SCREEN* screen = state->Screen( *instance ) )
654 displayed[screen->ConnectivityId()] = *instance;
655 }
656 }
657
658 if( const auto current = Keys().FindInstance( aSchematic.CurrentSheet().PathRef() ) )
659 {
660 if( SCH_SCREEN* screen = state->Screen( *current ) )
661 displayed[screen->ConnectivityId()] = *current;
662 }
663
664 std::set<ITEM_KEY, KEY_LESS> changed( KEY_LESS{ Keys() } );
665 const auto& publishedChanges = Published().Changes().changedItems;
666 changed.insert( publishedChanges.begin(), publishedChanges.end() );
668 aSchematic.NetChains().ApplyNetChainNetclasses();
669
670 const auto applyEnds = []( auto* aItem, auto aDangling )
671 {
672 const bool differs = aItem->IsStartDangling() != bool( aDangling & 1 )
673 || aItem->IsEndDangling() != bool( aDangling & 2 );
674 aItem->SetDanglingState( aDangling & 1, aDangling & 2 );
675 return differs;
676 };
677
678 for( const auto& [key, island] : Published().Auxiliary().Islands() )
679 {
680 SCH_SCREEN* screen = state->Screen( key.inst );
681
682 if( !screen )
683 continue;
684
685 const auto instance = displayed.find( screen->ConnectivityId() );
686
687 if( instance == displayed.end() || instance->second != key.inst )
688 continue;
689
690 for( const auto& [id, dangling] : island.value.dangling )
691 {
692 SCH_ITEM* item = screen->GetConnectivityItem( id );
693 bool differs = false;
694
695 if( !item )
696 continue;
697
698 switch( item->Type() )
699 {
700 case SCH_LINE_T:
701 differs = applyEnds( static_cast<SCH_LINE*>( item ), dangling );
702 break;
705 differs = applyEnds( static_cast<SCH_BUS_ENTRY_BASE*>( item ), dangling );
706 break;
707 case SCH_LABEL_T:
709 case SCH_HIER_LABEL_T:
711 case SCH_SHEET_PIN_T:
712 {
713 auto* label = static_cast<SCH_LABEL_BASE*>( item );
714 differs = label->SCH_LABEL_BASE::IsDangling() != bool( dangling );
715 label->SetIsDangling( dangling );
716
717 if( label->Type() == SCH_DIRECTIVE_LABEL_T )
718 {
719 auto* directive = static_cast<SCH_DIRECTIVE_LABEL*>( label );
720 std::unordered_set<SCH_RULE_AREA*> areas;
721 const auto& auxiliary = Published().Auxiliary();
722 const auto attached = auxiliary.RuleAreasOf().find( { id, key.inst } );
723
724 if( attached != auxiliary.RuleAreasOf().end() )
725 {
726 for( const KIID& areaId : attached->second )
727 {
728 if( auto* live = dynamic_cast<SCH_RULE_AREA*>( screen->GetConnectivityItem( areaId ) ) )
729 areas.insert( live );
730 }
731 }
732
733 if( directive->GetConnectedRuleAreas() != areas )
734 {
735 directive->ClearConnectedRuleAreas();
736
737 for( SCH_RULE_AREA* area : areas )
738 directive->AddConnectedRuleArea( area );
739
740 differs = true;
741 }
742 }
743 break;
744 }
745 case SCH_PIN_T:
746 differs = static_cast<SCH_PIN*>( item )->SetIsDangling( dangling );
747 break;
748 default:
749 break;
750 }
751
752 if( differs )
753 changed.insert( { id, key.inst } );
754 }
755 }
756
757 for( const auto& [id, instance] : displayed )
758 {
759 SCH_SCREEN* screen = state->Screen( instance );
760
761 if( !screen )
762 continue;
763
764 for( SCH_ITEM* item : screen->Items() )
765 {
766 item->SetConnectivityDirty( false );
767 item->RunOnChildren( []( SCH_ITEM* child ) { child->SetConnectivityDirty( false ); },
769 }
770 }
771
772 state->publicationApplied = true;
773
774 const uint64_t generation = state->resetGeneration;
775 const uint64_t dispatch = ++state->dispatchGeneration;
776 std::vector<uint64_t> listeners;
777 CHANGE_SET notification;
778
779 if( !state->listeners.empty() )
780 {
781 notification = state->engine.Published().Changes();
782 notification.changedItems.assign( changed.begin(), changed.end() );
783 std::ranges::copy( state->listeners | std::views::keys, std::back_inserter( listeners ) );
784 }
785
786 // A callback that clears or recalculates makes the rest of this delivery obsolete
787 const auto superseded = [&]()
788 {
789 return state->resetGeneration != generation || state->dispatchGeneration != dispatch;
790 };
791
792 // The publication is already applied, so one failing callback must not starve the others
793 const auto deliver = []( const char* aKind, const auto& aCall )
794 {
795 try
796 {
797 aCall();
798 }
799 catch( const std::exception& error )
800 {
801 wxLogTrace( "KICAD_CONNECTIVITY", "Connectivity %s failed: %s", aKind, error.what() );
802 }
803 catch( ... )
804 {
805 wxLogTrace( "KICAD_CONNECTIVITY", "Connectivity %s failed with an unknown exception", aKind );
806 }
807 };
808
809 if( handler )
810 {
811 for( const ITEM_KEY& key : changed )
812 {
813 if( superseded() )
814 break;
815
816 if( SCH_ITEM* item = state->Resolve( key ) )
817 deliver( "change handler",
818 [&]()
819 {
820 handler( item );
821 } );
822 }
823 }
824
825 if( !notification.Empty() )
826 {
827 for( uint64_t id : listeners )
828 {
829 if( superseded() )
830 break;
831
832 const auto found = state->listeners.find( id );
833
834 if( found != state->listeners.end() )
835 {
836 const auto listener = found->second;
837 deliver( "listener",
838 [&]()
839 {
840 listener( notification );
841 } );
842 }
843 }
844 }
845}
846
847
848void FACADE::Update( SCHEMATIC& aSchematic, bool aRebuild )
849{
850 wxASSERT( wxThread::IsMain() );
851
852 try
853 {
854 updateModel( aSchematic, aSchematic.BuildSheetListSortedByPageNumbers(), aRebuild );
855 }
856 catch( ... )
857 {
858 Clear();
859 throw;
860 }
861}
862
863bool FACADE::UpdateShadow( SCHEMATIC& aSchematic, const SCH_SHEET_LIST& aPaths, bool aRebuild )
864{
865 try
866 {
867 updateModel( aSchematic, aPaths, aRebuild );
868 return true;
869 }
870 catch( const std::exception& error )
871 {
872 Clear();
873 wxLogTrace( "CONN_SHADOW", "Shadow connectivity failed: %s", error.what() );
874 }
875 catch( ... )
876 {
877 Clear();
878 wxLogTrace( "CONN_SHADOW", "Shadow connectivity failed with an unknown exception" );
879 }
880
881 return false;
882}
883
884
885void FACADE::ApplyNetclasses( NET_SETTINGS& aSettings ) const
886{
887 wxASSERT( wxThread::IsMain() );
888 std::map<wxString, std::set<wxString>> assignments;
889
890 for( const auto& [name, classes] : Published().Netclasses() )
891 {
892 auto& names = assignments[Keys().Name( name )];
893
894 for( NAME_ID netclass : *classes )
895 names.insert( Keys().Name( netclass ) );
896 }
897
898 std::vector<wxString> retired;
899
900 for( const auto& [name, classes] : aSettings.GetNetclassLabelAssignments() )
901 {
902 if( !assignments.contains( name ) )
903 retired.push_back( name );
904 }
905
906 for( const wxString& name : retired )
907 {
908 aSettings.ClearCacheForNet( name );
910 }
911
912 const auto& previous = aSettings.GetNetclassLabelAssignments();
913
914 for( const auto& [name, classes] : assignments )
915 {
916 const auto old = previous.find( name );
917
918 if( old == previous.end() || old->second != classes )
919 {
920 aSettings.SetNetclassLabelAssignment( name, classes );
921 aSettings.ClearCacheForNet( name );
922 }
923 }
924}
925
926
927void FACADE::updateModel( SCHEMATIC& aSchematic, const SCH_SHEET_LIST& aPaths, bool aRebuild )
928{
929 wxASSERT( wxThread::IsMain() );
930
931 TEXT_ENVIRONMENT environment;
932 INPUT_TEXT_SCOPE frame( environment );
933 auto context = CaptureTextContext( aSchematic, aPaths );
934 const bool changed = !context || m_state->textContext != context
935 || m_state->engine.ExternalSourcesChanged( aPaths, true );
936
937 if( changed && m_state->textEpoch == std::numeric_limits<uint64_t>::max() )
938 throw std::overflow_error( "Connectivity text epoch exhausted" );
939
940 BUS_ALIASES aliases;
941
942 for( const auto& alias : aSchematic.GetAllBusAliases() )
943 {
944 if( alias )
945 aliases.insert_or_assign( alias->GetName(), alias->Members() );
946 }
947
948 Update( aPaths, m_state->textEpoch + ( changed ? 1 : 0 ), aliases, aRebuild );
949 m_state->textContext = std::move( context );
950}
951
952void FACADE::Update( const SCH_SHEET_LIST& aPaths, uint64_t aTextEpoch, const BUS_ALIASES& aAliases, bool aRebuild )
953{
954 wxASSERT( wxThread::IsMain() );
955
956 m_state->textContext.reset();
957 m_state->publicationApplied = false;
958 m_state->textChecks.reset();
959 m_state->simulationModels.reset();
960
961 try
962 {
963 m_state->engine.Update( aPaths, aTextEpoch, aAliases, aRebuild );
964 decltype( m_state->sources ) sources;
965
966 for( const SCH_SHEET_PATH& path : aPaths )
967 {
968 if( SCH_SCREEN* screen = path.LastScreen() )
969 {
970 if( const auto instance = Keys().FindInstance( path.PathRef() ) )
971 sources.emplace( *instance, screen->ConnectivitySource() );
972 }
973 }
974
975 m_state->RefreshNetItems();
976 m_state->sources.swap( sources );
977 m_state->textEpoch = aTextEpoch;
978 }
979 catch( ... )
980 {
981 Clear();
982 throw;
983 }
984}
985
987{
988 m_state->publicationApplied = false;
989 m_state->textChecks.reset();
990 m_state->simulationModels.reset();
991 ++m_state->resetGeneration;
992 m_state->sources.clear();
993 m_state->netItems.clear();
994 m_state->engine.Clear();
995 m_state->textEpoch = 0;
996 m_state->textContext.reset();
997}
998
1000{
1001 wxASSERT( wxThread::IsMain() );
1002 ++aFacade.m_state->publicationHolds;
1003}
1004
1006{
1007 if( const auto state = m_state.lock() )
1008 --state->publicationHolds;
1009}
1010
1011const PUBLICATION& FACADE::Published() const { return m_state->engine.Published(); }
1012const SESSION_KEYS& FACADE::Keys() const { return m_state->engine.Keys(); }
1013const ENGINE& FACADE::Engine() const { return m_state->engine; }
1014
1015void FACADE::PrepareTextChecks( SCHEMATIC& aSchematic, bool aIncludeDrawingSheet )
1016{
1017 wxASSERT( wxThread::IsMain() );
1018 m_state->textChecks.reset();
1019 const auto paths = m_state->SourcePaths( aSchematic );
1020 const auto vcs = sourceProjectContext( aSchematic );
1021 std::optional<DS_DATA_MODEL> isolated;
1022 struct RESTORE_LAYOUT
1023 {
1024 DS_DATA_MODEL* previous = nullptr;
1025 ~RESTORE_LAYOUT()
1026 {
1027 if( previous )
1029 }
1030 } restoreLayout;
1031
1032 if( aIncludeDrawingSheet )
1033 {
1034 // Rendering replaces draw objects that the canvas may still reference
1036 wxString layout;
1037 original.SaveInString( &layout );
1038 isolated.emplace();
1039 restoreLayout.previous = &original;
1040 DS_DATA_MODEL::SetAltInstance( &*isolated );
1041 isolated->SetPageLayout( layout.utf8_str() );
1042 isolated->AllowVoidList( original.VoidListAllowed() );
1043 }
1044
1045 std::map<KIID_PATH, FACADE_STATE::TEXT_CHECKS> captured;
1046
1047 for( const SCH_SHEET_PATH& path : paths )
1048 {
1049 auto& checks = captured[path.PathRef()];
1050
1051 if( aIncludeDrawingSheet )
1052 checks.drawingSheet.emplace();
1053
1054 if( const SCH_SCREEN* screen = path.LastScreen() )
1055 {
1056 checks.items = ExtractTextChecks( *screen, path );
1057
1058 if( aIncludeDrawingSheet )
1059 checks.drawingSheet = ExtractDrawingSheetTextChecks( *screen, path );
1060 }
1061 }
1062
1063 m_state->textChecks = std::move( captured );
1064}
1065
1066std::vector<TEXT_CHECK_FACT> FACADE::TextChecks( const KIID_PATH& aPath, bool aDrawingSheet ) const
1067{
1068 wxASSERT( wxThread::IsMain() );
1069
1070 if( !m_state->textChecks )
1071 throw std::logic_error( "Text sources have not been prepared" );
1072
1073 const auto& captured = m_state->textChecks->at( aPath );
1074
1075 if( aDrawingSheet && !captured.drawingSheet )
1076 throw std::logic_error( "Drawing sheet text sources have not been prepared" );
1077
1078 return aDrawingSheet ? *captured.drawingSheet : captured.items;
1079}
1080
1082{
1083 wxASSERT( wxThread::IsMain() );
1084 m_state->simulationModels.reset();
1085 const auto vcs = sourceProjectContext( aSchematic );
1086
1087 std::map<KIID_PATH, std::vector<SIMULATION_MODEL_FACT>> captured;
1088 const wxString variant = aSchematic.GetCurrentVariant();
1089
1090 for( const SCH_SHEET_PATH& path : m_state->SourcePaths( aSchematic ) )
1091 captured.emplace( path.PathRef(), ExtractSimulationModelFacts( path, variant ) );
1092
1093 m_state->simulationModels = std::move( captured );
1094}
1095
1096std::vector<SIMULATION_MODEL_FACT> FACADE::SimulationModels( const KIID_PATH& aPath ) const
1097{
1098 wxASSERT( wxThread::IsMain() );
1099
1100 if( !m_state->simulationModels )
1101 throw std::logic_error( "Simulation sources have not been prepared" );
1102
1103 return m_state->simulationModels->at( aPath );
1104}
1105
1106std::optional<ITEM_VIEW> FACADE::Connection( const KIID& aItem, const KIID_PATH& aPath ) const
1107{
1108 const auto instance = Keys().FindInstance( aPath );
1109
1110 if( !instance )
1111 return std::nullopt;
1112
1113 const ITEM_KEY key{ aItem, *instance };
1114
1115 if( !m_state->Row( key ) )
1116 return std::nullopt;
1117
1118 return ITEM_VIEW( m_state, key );
1119}
1120
1121wxString ITEM_VIEW::Name( bool aIgnoreSheet ) const
1122{
1123 const auto state = m_state.lock();
1124 const auto* row = state ? state->Row( m_item ) : nullptr;
1125 return row ? state->Name( state->engine.Published().Components().at( row->component ), aIgnoreSheet ) : wxString();
1126}
1127
1128wxString ITEM_VIEW::LocalName() const
1129{
1130 const auto state = m_state.lock();
1131 const auto* row = state ? state->Row( m_item ) : nullptr;
1132 return row && row->localName != INVALID_ID ? state->engine.Keys().Name( row->localName ) : wxString();
1133}
1134
1136{
1137 const auto state = m_state.lock();
1138 const auto* row = state ? state->Row( m_item ) : nullptr;
1139 return row && row->fullLocalName != INVALID_ID ? state->engine.Keys().Name( row->fullLocalName ) : wxString();
1140}
1141
1143{
1144 const auto state = m_state.lock();
1145 const auto* row = state ? state->Row( m_item ) : nullptr;
1146 return row && row->kind == KIND::BUNDLE;
1147}
1148
1150{
1151 const auto state = m_state.lock();
1152 const auto* row = state ? state->Row( m_item ) : nullptr;
1153 return row && row->kind == KIND::SIGNAL;
1154}
1155
1157{
1158 const auto state = m_state.lock();
1159 const auto* row = state ? state->Row( m_item ) : nullptr;
1160 return !row || !row->driver;
1161}
1162
1164{
1165 const auto state = m_state.lock();
1166 const auto* row = state ? state->Row( m_item ) : nullptr;
1167 return row ? row->netCode : 0;
1168}
1169
1171{
1172 const auto state = m_state.lock();
1173 const auto* row = state ? state->Row( m_item ) : nullptr;
1174 return row ? row->subgraphCode : 0;
1175}
1176
1178{
1179 const auto state = m_state.lock();
1180 const auto* row = state ? state->Row( m_item ) : nullptr;
1181 return row && row->driver ? state->Resolve( *row->driver ) : nullptr;
1182}
1183
1184std::vector<SCH_ITEM*> ITEM_VIEW::ConnectedItems() const
1185{
1186 std::vector<SCH_ITEM*> result;
1187 const auto state = m_state.lock();
1188
1189 if( !state || !state->Row( m_item ) )
1190 return result;
1191
1192 const auto& neighbors = state->engine.Published().Auxiliary().NeighborsOf();
1193 const auto found = neighbors.find( m_item );
1194
1195 if( found == neighbors.end() )
1196 return result;
1197
1198 result.reserve( found->second.size() );
1199
1200 for( const KIID& id : found->second )
1201 {
1202 if( SCH_ITEM* item = state->Resolve( { id, m_item.inst } ) )
1203 result.push_back( item );
1204 }
1205
1206 return result;
1207}
1208
1210{
1211 const auto state = m_state.lock();
1212 const auto* row = state ? state->Row( m_item ) : nullptr;
1213 return row ? state->Sheet( row->driver ) : KIID_PATH();
1214}
1215
1217{
1218 const auto state = m_state.lock();
1219 const auto* row = state ? state->Row( m_item ) : nullptr;
1220 return row ? state->Members( row->component, m_item.inst, m_state, row ) : BUS_MEMBERS();
1221}
1222
1223std::optional<NET_VIEW> FACADE::GetSubgraphForItem( const KIID& aItem, const KIID_PATH& aPath ) const
1224{
1225 const auto instance = Keys().FindInstance( aPath );
1226
1227 if( !instance )
1228 return std::nullopt;
1229
1230 const auto* row = m_state->Row( { aItem, *instance } );
1231
1232 if( !row )
1233 return std::nullopt;
1234
1235 return NET_VIEW( m_state, row->component, *instance );
1236}
1237
1238std::vector<NET_GROUP> FACADE::GetNetMap() const
1239{
1240 const auto live = m_state->LiveInstances();
1241 std::vector<NET_GROUP> result;
1242 result.reserve( Published().ByName().size() );
1243
1244 for( const auto& [name, node] : Published().ByName() )
1245 {
1246 if( auto group = m_state->Group( node, live, m_state ) )
1247 result.push_back( std::move( *group ) );
1248 }
1249
1250 return result;
1251}
1252
1253std::vector<wxString> FACADE::NetNames() const
1254{
1255 const auto live = m_state->LiveInstances();
1256 std::vector<wxString> result;
1257 result.reserve( Published().ByName().size() );
1258
1259 for( const auto& [name, node] : Published().ByName() )
1260 {
1261 if( m_state->AnyLive( node, live ) )
1262 result.push_back( Keys().Name( name ) );
1263 }
1264
1265 return result;
1266}
1267
1268
1269std::optional<NET_GROUP> FACADE::NetByName( const wxString& aName ) const
1270{
1271 const auto node = Published().FindByName( aName );
1272
1273 if( !node )
1274 return std::nullopt;
1275
1276 return m_state->Group( *node, m_state->LiveInstances(), m_state );
1277}
1278
1279
1280std::vector<NET_GROUP> FACADE::BusWithMembers( const wxString& aName ) const
1281{
1282 const auto node = m_state->BusNode( aName );
1283
1284 if( !node )
1285 return {};
1286
1287 const auto live = m_state->LiveInstances();
1288
1289 if( !m_state->AnyLive( *node, live ) )
1290 return {};
1291
1292 const auto members = Published().MembersOf( *node );
1293 std::vector<NODE_ID> nodes( members.begin(), members.end() );
1294 nodes.push_back( *node );
1295 return m_state->Groups( std::move( nodes ), live, m_state );
1296}
1297
1298std::vector<NET_GROUP> FACADE::BundlesOf( NODE_ID aSignal ) const
1299{
1300 const auto parents = Published().ParentsOf( aSignal );
1301
1302 if( parents.empty() )
1303 return {};
1304
1305 const auto live = m_state->LiveInstances();
1306
1307 if( !m_state->AnyLive( aSignal, live ) )
1308 return {};
1309
1310 return m_state->Groups( { parents.begin(), parents.end() }, live, m_state );
1311}
1312
1313std::vector<wxString> FACADE::GetEquivalentBusNames( const wxString& aName ) const
1314{
1315 const auto node = m_state->BusNode( aName );
1316
1317 if( !node )
1318 return {};
1319
1320 const auto live = m_state->LiveInstances();
1321
1322 if( !m_state->AnyLive( *node, live ) )
1323 return {};
1324
1325 auto names = Published().EquivalentBusNames( aName );
1326 std::erase_if( names, [&]( const wxString& name )
1327 {
1328 const auto equivalent = Published().FindByName( name );
1329 return !equivalent || !m_state->AnyLive( *equivalent, live );
1330 } );
1331 return names;
1332}
1333
1335{
1336 const auto state = m_state.lock();
1337 return state ? state->engine.Keys().Instance( m_instance ) : KIID_PATH();
1338}
1339
1340wxString NET_VIEW::Name( bool aIgnoreSheet ) const
1341{
1342 const auto state = m_state.lock();
1343 const auto* net = state ? state->Net( m_component, m_instance ) : nullptr;
1344 return net ? state->Name( *net, aIgnoreSheet ) : wxString();
1345}
1346
1348{
1349 const auto state = m_state.lock();
1350 const auto* net = state ? state->Net( m_component, m_instance ) : nullptr;
1351 return net && net->content->kind == KIND::BUNDLE;
1352}
1353
1355{
1356 const auto state = m_state.lock();
1357 const auto* net = state ? state->Net( m_component, m_instance ) : nullptr;
1358 return net && net->content->kind == KIND::SIGNAL;
1359}
1360
1362{
1363 const auto state = m_state.lock();
1364 const auto* net = state ? state->Net( m_component, m_instance ) : nullptr;
1365 return net && net->content->best ? state->Resolve( net->content->best->source ) : nullptr;
1366}
1367
1369{
1370 const auto state = m_state.lock();
1371 const auto* net = state ? state->Net( m_component, m_instance ) : nullptr;
1372 return net && net->content->best ? state->Sheet( net->content->best->source ) : KIID_PATH();
1373}
1374
1376{
1377 const auto state = m_state.lock();
1378 return state && state->Net( m_component, m_instance )
1379 ? state->Members( m_component, m_instance, m_state ) : BUS_MEMBERS();
1380}
1381
1382std::vector<SCH_ITEM*> NET_VIEW::Items() const
1383{
1384 std::vector<SCH_ITEM*> result;
1385 const auto state = m_state.lock();
1386
1387 if( !state || !state->Net( m_component, m_instance ) )
1388 return result;
1389
1390 const auto& ids = state->netItems.at( m_component )->instances.at( m_instance );
1391 result.reserve( ids.size() );
1392
1393 for( const KIID& id : ids )
1394 {
1395 if( SCH_ITEM* item = state->Resolve( { id, m_instance } ) )
1396 result.push_back( item );
1397 }
1398
1399 return result;
1400}
1401} // namespace SCH_CONNECTIVITY
const char * name
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
Handle the graphic items list to draw/plot the frame and title block.
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
bool VoidListAllowed()
void SaveInString(wxString *aOutputString)
Save the description in a buffer.
static void SetAltInstance(DS_DATA_MODEL *aLayout=nullptr)
Set an alternate instance of DS_DATA_MODEL.
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Definition kiid.h:46
NET_SETTINGS stores various net-related settings in a project context.
void ClearCacheForNet(const wxString &netName)
Clears the net cache and cached bus classes derived from that net.
void ClearNetclassLabelAssignment(const wxString &netName)
Clears a specific net name to netclass assignment Calling user is responsible for resetting the effec...
void SetNetclassLabelAssignment(const wxString &netName, const std::set< wxString > &netclasses)
Sets a net name to netclasses assignment Calling user is responsible for resetting the effective netc...
const std::map< wxString, std::set< wxString > > & GetNetclassLabelAssignments() const
Gets all current net name to netclasses assignments.
wxString GetTypeAsString() const
const VECTOR2D & GetSizeMils() const
Definition page_info.h:145
bool IsPortrait() const
Definition page_info.h:123
std::shared_ptr< NET_SETTINGS > & NetSettings()
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:271
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:277
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
Holds all the data relating to one schematic.
Definition schematic.h:153
SCH_CONNECTIVITY::NETCHAIN_MANAGER & NetChains() const
Definition schematic.h:397
wxString GetVariantDescription(const wxString &aVariantName) const
Return the description for a variant.
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
const std::vector< std::shared_ptr< BUS_ALIAS > > & GetAllBusAliases() const
Definition schematic.h:429
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:175
wxString GetCurrentVariant() const
Return the current variant being edited.
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:369
SCH_SHEET & Root() const
Definition schematic.h:280
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:384
std::map< std::pair< KIID, KIID_PATH >, wxString > PackageRefSuffixes() const
Symbol and sheet path of each instance whose package suffix is another sheet's, with that suffix.
Base class for a bus or wire entry.
const AREA_INDEX & RuleAreasOf() const
Owns one key/version session and its current stage evaluations; main-thread use only.
Model-owned source resolver.
std::optional< NET_GROUP > NetByName(const wxString &aName) const
void Update(SCHEMATIC &aSchematic, bool aRebuild=false)
std::optional< NET_VIEW > GetSubgraphForItem(const KIID &aItem, const KIID_PATH &aPath) const
void Recalculate(SCHEMATIC &aSchematic, bool aRebuild=false, const std::function< void(SCH_ITEM *)> &aChangedHandler={})
Update the complete model and apply netclasses, dangling flags and dirty-state clearing.
void ApplyNetclasses(NET_SETTINGS &aSettings) const
Replace label-derived assignments from a complete publication; invalidate changed net caches.
std::vector< NET_GROUP > GetNetMap() const
Owned query-time groups in canonical name order, with unique instances in KIID_PATH order.
std::vector< TEXT_CHECK_FACT > TextChecks(const KIID_PATH &aPath, bool aDrawingSheet=false) const
std::vector< wxString > NetNames() const
const SESSION_KEYS & Keys() const
void PrepareSimulationModels(SCHEMATIC &aSchematic)
const ENGINE & Engine() const
void updateModel(SCHEMATIC &aSchematic, const SCH_SHEET_LIST &aPaths, bool aRebuild)
Capture the text context and advance the text epoch when it or an external source changed.
void PrepareTextChecks(SCHEMATIC &aSchematic, bool aIncludeDrawingSheet=false)
std::vector< SIMULATION_MODEL_FACT > SimulationModels(const KIID_PATH &aPath) const
bool UpdateShadow(SCHEMATIC &aSchematic, const SCH_SHEET_LIST &aPaths, bool aRebuild=false)
std::optional< ITEM_VIEW > Connection(const KIID &aItem, const KIID_PATH &aPath) const
const PUBLICATION & Published() const
SUBSCRIPTION Subscribe(std::function< void(const CHANGE_SET &)> aListener)
std::vector< wxString > GetEquivalentBusNames(const wxString &aName) const
std::vector< NET_GROUP > BundlesOf(NODE_ID aSignal) const
std::shared_ptr< FACADE_STATE > m_state
std::vector< NET_GROUP > BusWithMembers(const wxString &aName) const
Excludes derived graph text and shares dynamic source values through nested resolvers.
Definition conn_text.h:34
BUS_MEMBERS Members() const
wxString Name(bool aIgnoreSheet=false) const
std::weak_ptr< const FACADE_STATE > m_state
Definition conn_facade.h:63
std::vector< SCH_ITEM * > ConnectedItems() const
void Rebuild(const NETCHAIN_INPUT &aConnectivity, const std::function< void(NETCHAIN_MANAGER &)> &aBeforePublish={})
void ApplyNetChainNetclasses()
Mirror each committed net chain's netclass override into the project NET_SETTINGS as a chain-derived ...
wxString Name(bool aIgnoreSheet=false) const
BUS_MEMBERS Members() const
SCH_ITEM * Driver() const
std::vector< SCH_ITEM * > Items() const
std::weak_ptr< const FACADE_STATE > m_state
Definition conn_facade.h:91
PUBLICATION_HOLD(const FACADE &aFacade)
std::weak_ptr< FACADE_STATE > m_state
Main-thread publication.
std::span< const NODE_ID > MembersOf(NODE_ID aBundle) const
std::vector< wxString > EquivalentBusNames(const wxString &aName) const
const AUXILIARY & Auxiliary() const
std::span< const NODE_ID > ParentsOf(NODE_ID aSignal) const
const CHANGE_SET & Changes() const
std::optional< NODE_ID > FindByName(const wxString &aName) const
static bool Active()
True inside a render scope, unless an INPUT_TEXT_SCOPE is also active.
Definition conn_text.h:69
Session IDs are dense handles, never a canonical ordering.
Definition conn_keys.h:146
const wxString & Name(NAME_ID aId) const
Definition conn_keys.h:163
const KIID_PATH & Instance(INST_ID aId) const
Definition conn_keys.h:162
std::optional< INST_ID > FindInstance(const KIID_PATH &aPath) const
Definition conn_keys.h:160
Main-thread registration owner.
SUBSCRIPTION & operator=(const SUBSCRIPTION &)=delete
std::weak_ptr< FACADE_STATE > m_state
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.h:287
void SetConnectivityDirty(bool aDirty=true)
Set the dirty flag.
Definition sch_item.cpp:314
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
static wxString MakeKey(const wxString &aName, uint32_t aComponent)
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:144
uint64_t ConnectivityId() const
Process-local lifetime identity; file UUIDs can be shared by distinct screens.
Definition sch_screen.h:201
uint64_t ConnectivityRevision() const
The only change signal that the connectivity engine reads from this screen.
Definition sch_screen.h:181
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
std::weak_ptr< const CONNECTIVITY_SOURCE > ConnectivitySource() const
Definition sch_screen.h:208
SCH_ITEM * GetConnectivityItem(const KIID &aId) const
Resolve a drawing item or a connectable child on this screen; ambiguous IDs return null.
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:168
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
const KIID_PATH & PathRef() const
Borrow the cached path until this sheet path is modified or destroyed.
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:93
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
int CountSheets() const
Count the number of sheets found in "this" sheet including all of the subsheets.
bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flags.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition sch_sheet.h:519
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition sch_sheet.h:563
const wxString & GetCompany() const
Definition title_block.h:93
const wxString & GetRevision() const
Definition title_block.h:83
const wxString & GetDate() const
Definition title_block.h:73
const wxString & GetComment(int aIdx) const
const wxString & GetTitle() const
Definition title_block.h:60
bool equivalent(SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b)
@ NO_RECURSE
Definition eda_item.h:52
void Reset() override
std::string source
Value keys and the key session of the schematic connectivity engine.
std::map< wxString, std::vector< wxString > > BUS_ALIASES
Bus alias table, from alias name to member texts.
Definition conn_bus.h:40
uint32_t INST_ID
Session handle of a sheet instance KIID_PATH.
Definition conn_keys.h:41
std::vector< SIMULATION_MODEL_FACT > ExtractSimulationModelFacts(const SCH_SHEET_PATH &aPath, const wxString &aVariantName)
std::vector< TEXT_CHECK_FACT > ExtractTextChecks(const SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath)
uint32_t NAME_ID
Session handle of a name, ordered by UTF-8 value through NAME_LESS.
Definition conn_keys.h:42
uint64_t SCREEN_ID
Process-local SCH_SCREEN::ConnectivityId(), never a file UUID.
Definition conn_keys.h:44
uint32_t NODE_ID
Session handle of a NODE_KEY graph node.
Definition conn_keys.h:43
std::vector< TEXT_CHECK_FACT > ExtractDrawingSheetTextChecks(const SCH_SCREEN &aScreen, const SCH_SHEET_PATH &aPath)
TEXT_EVAL::ENVIRONMENT TEXT_ENVIRONMENT
Definition conn_text.h:27
BUS_ALIGNMENT Align(const BUS_SCHEMA &aLeft, const BUS_SCHEMA &aRight)
Match the leaves of aLeft to the leaves of aRight.
Definition conn_bus.cpp:282
constexpr uint32_t INVALID_ID
Marks an unset handle.
Definition conn_keys.h:47
wxString GetContextPath()
Return the current context path for repo-scoped VCS queries.
bool GetContextIsFile()
File/directory classification captured when the current context was activated.
STL namespace.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
Member correspondence between two bus schemas.
Definition conn_bus.h:181
std::vector< size_t > unmappedLeft
Left leaf ordinals without a right partner.
Definition conn_bus.h:183
std::vector< std::pair< size_t, size_t > > matched
Pairs of left and right leaf ordinals.
Definition conn_bus.h:182
Query-time snapshot; reacquire after Update.
Difference between two publications.
std::vector< ITEM_KEY > changedItems
Items whose row, island, rule area, source or text changed.
std::shared_ptr< const COMPONENT_CONTENT > content
std::map< INST_ID, std::vector< KIID > > instances
std::optional< std::vector< TEXT_CHECK_FACT > > drawingSheet
std::vector< TEXT_CHECK_FACT > items
std::map< NODE_ID, std::shared_ptr< const NET_ITEMS > > netItems
std::optional< NODE_ID > BusNode(const wxString &aName) const
std::vector< NET_GROUP > Groups(std::vector< NODE_ID > aNodes, std::span< const INST_ID > aLive, const std::weak_ptr< const FACADE_STATE > &aState) const
std::vector< INST_ID > LiveInstances() const
const ITEM_RESULT * Row(const ITEM_KEY &aItem) const
KIID_PATH Sheet(const std::optional< ITEM_KEY > &aDriver) const
const PUBLISHED_COMPONENT * Net(NODE_ID aNode, INST_ID aInstance) const
std::optional< NET_GROUP > Group(NODE_ID aNode, std::span< const INST_ID > aLive, const std::weak_ptr< const FACADE_STATE > &aState) const
wxString Name(const PUBLISHED_COMPONENT &aComponent, bool aIgnoreSheet) const
std::map< INST_ID, std::weak_ptr< const SCH_SCREEN::CONNECTIVITY_SOURCE > > sources
bool AnyLive(NODE_ID aNode, std::span< const INST_ID > aLive) const
std::map< uint64_t, std::function< void(const CHANGE_SET &)> > listeners
BUS_MEMBERS Members(NODE_ID aNode, INST_ID aInstance, const std::weak_ptr< const FACADE_STATE > &aState, const ITEM_RESULT *aRow=nullptr) const
bool Current(const SCH_SCREEN &aScreen) const
SCH_SCREEN * Screen(const std::weak_ptr< const SCH_SCREEN::CONNECTIVITY_SOURCE > &aSource) const
SCH_ITEM * Resolve(const ITEM_KEY &aItem) const
std::optional< MODEL_TEXT_CONTEXT > textContext
SCH_SHEET_LIST SourcePaths(SCHEMATIC &aSchematic) const
std::optional< std::map< KIID_PATH, std::vector< SIMULATION_MODEL_FACT > > > simulationModels
SCH_SCREEN * Screen(INST_ID aInstance) const
std::optional< std::map< KIID_PATH, TEXT_CHECKS > > textChecks
One item or pin in one sheet instance.
Definition conn_keys.h:77
KIID item
The item or pin KIID.
Definition conn_keys.h:78
INST_ID inst
The sheet instance that shows the item.
Definition conn_keys.h:79
Published connection of one item on one sheet instance.
Orders keys by value through SESSION_KEYS::Less().
Definition conn_keys.h:255
Orders name handles by UTF-8 value.
Definition conn_keys.h:246
Immediate-use netchain input; shared-screen items are qualified by their instance.
std::pmr::monotonic_buffer_resource storage
One published net or bus with its identity.
NAME_ID name
Unique name, invalid without a claim.
std::shared_ptr< const COMPONENT_CONTENT > content
One member position of a bus.
Definition conn_keys.h:127
ITEM_KEY bundleDriver
The source item of the claim that defines the leaf order.
Definition conn_keys.h:128
std::vector< DATABASE_CONNECTION::ROW > rows
std::string path
wxString result
Test unit parsing edge cases and error handling.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:158
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< double > VECTOR2D
Definition vector2d.h:707