38 bool powerFlag =
false;
46bool isLabelOrSheetPin(
KICAD_T aType )
52const ITEM_FACT* findFact(
const std::vector<ITEM_FACT>& aFacts,
const KIID& aId )
54 const auto it = std::ranges::lower_bound( aFacts, aId, [](
const KIID& a,
const KIID& b ) {
return a < b; },
56 return it != aFacts.end() && it->id == aId ? &*it :
nullptr;
59template <
typename MAP,
typename BUILD>
60const typename MAP::mapped_type& cached( MAP& aCache,
const typename MAP::key_type& aKey, BUILD&& aBuild )
62 if(
const auto it = aCache.find( aKey ); it != aCache.end() )
65 return aCache.emplace( aKey, aBuild() ).first->second;
68template <
typename MAP>
69std::vector<typename MAP::mapped_type> moveValues( MAP& aMap )
71 std::vector<typename MAP::mapped_type>
result;
72 result.reserve( aMap.size() );
74 for(
auto& [key, value] : aMap )
75 result.push_back( std::move( value ) );
82 std::set<INST_ID> seen;
83 std::vector<INST_ID>
result;
85 for(
const auto& [key, island] : aIslands )
87 if( seen.insert( key.inst ).second )
88 result.push_back( key.inst );
94std::map<KIID, VECTOR2I> itemPositions(
const SCREEN_FACTS& aFacts )
96 std::map<KIID, VECTOR2I>
result;
98 for(
const ITEM_FACT& fact : aFacts.items )
100 if( !fact.ports.empty() )
101 result.emplace( fact.id, fact.ports.front().position );
111std::map<KIID, NC_SOURCE> noConnectSources(
const SCREEN_FACTS& aFacts )
113 std::map<KIID, NC_SOURCE>
result;
115 for(
const ITEM_FACT& fact : aFacts.items )
119 result.emplace(
pin.id, NC_SOURCE{ SCH_PIN_T, pin.position, true, pin.type == ELECTRICAL_PINTYPE::PT_NC,
120 ( pin.globalPowerParent || pin.localPowerParent )
121 && pin.type == ELECTRICAL_PINTYPE::PT_POWER_OUT } );
124 if( !fact.ports.empty() )
125 result.emplace( fact.id, NC_SOURCE{ fact.type, fact.ports.front().position } );
131std::set<std::pair<NODE_ID, INST_ID>> busNoConnectMembers(
const PUBLICATION& aPublished )
133 std::set<std::pair<NODE_ID, INST_ID>>
result;
135 for(
const auto& [key, island] : aPublished.Auxiliary().Islands() )
137 if( !island.value.atoms.busNoConnect )
140 const auto& row = aPublished.Rows().at( { *island.value.atoms.noConnect, key.inst } );
141 const auto& bundle = *aPublished.Components().at( row.component ).content;
143 for(
const SLOT_KEY& slot : bundle.slots )
144 result.emplace( aPublished.SlotComponents().at( slot ), key.inst );
153 std::vector<INST_ID>
result;
154 std::ranges::copy( std::views::keys(
m_inputs.Instances().Entries() ), std::back_inserter(
result ) );
157 return std::tie(
m_inputs.FindInstance(
id )->value.pageOrder,
m_keys.Instance(
id ) );
164 std::map<INST_ID, size_t> rank;
167 rank.emplace( instance, rank.size() );
169 std::map<std::pair<SCREEN_ID, KIID>,
RECORD_KEY> driven;
172 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
174 const auto& claims =
m_records.Records().Entries().at( key )->value.claims;
182 const std::pair<SCREEN_ID, KIID> driver{
m_inputs.InstanceScreen( key.inst ), claims.front().source.item };
183 const auto [first, inserted] = driven.try_emplace( driver, key );
185 if( !inserted && rank.at( key.inst ) < rank.at( first->second.inst ) )
189 for(
const auto& [driver, key] : driven )
197 wxASSERT( wxThread::IsMain() );
198 std::vector<DRIVER_CONFLICT>
result;
199 std::map<INST_ID, std::map<KIID, VECTOR2I>> positions;
202 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
204 if( island.value.atoms.strongNames.size() < 2 || !reported.contains( key ) )
207 const auto& claims =
m_records.Records().Entries().at( key )->value.claims;
208 const CLAIM& first = claims.front();
209 const auto second = std::ranges::find_if( claims,
212 if( second == claims.end() )
215 const auto& points = cached( positions, key.inst,
216 [&] { return itemPositions( m_inputs.InstanceScreenFacts( key.inst ) ); } );
220 points.at( second->source.item ) } );
228 wxASSERT( wxThread::IsMain() );
229 std::vector<WIRE_ENDPOINT>
result;
232 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
234 if( !reported.contains( key ) )
237 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
239 for(
const auto& [item, dangling] : island.value.dangling )
241 const ITEM_FACT* fact = dangling ? findFact( facts, item ) :
nullptr;
250 if( !entry && !wire )
253 for(
size_t endpoint = 0; endpoint < fact->
ports.size(); ++endpoint )
255 if( !( dangling & ( 1U << endpoint ) ) )
259 if( endpoint != 0 && ( dangling & 1 )
260 && fact->
ports[endpoint].position == fact->
ports.front().position )
265 result.push_back( {
m_keys.Instance( key.inst ), item, fact->
ports[endpoint].position, entry } );
275 wxASSERT( wxThread::IsMain() );
276 std::vector<FLOATING_WIRE>
result;
278 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
280 const auto& row =
m_published.Rows().at( { island.value.items.front(), key.inst } );
282 if(
m_published.Components().at( row.component ).content->best )
285 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
288 for(
const KIID& item : island.value.items )
290 const ITEM_FACT* fact = findFact( facts, item );
292 if( !fact || fact->
ports.empty() )
300 if(
group.items.empty() )
301 group.position = fact->
ports.front().position;
303 group.items.push_back( item );
305 if(
group.items.size() == 4 )
309 if( !
group.items.empty() )
318 wxASSERT( wxThread::IsMain() );
319 std::vector<BUS_NET_CONFLICT>
result;
322 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
324 if( !island.value.atoms.kindConflict || !reported.contains( key ) )
327 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
328 const auto& claims =
m_records.Records().Entries().at( key )->value.claims;
332 for(
const KIID&
id : island.value.items )
334 const ITEM_FACT* fact = findFact( facts,
id );
336 if( !fact || fact->
ports.empty() )
345 else if( isLabelOrSheetPin( fact->
type ) )
347 const auto claim = std::ranges::find_if( claims,
348 [&](
const CLAIM& candidate ) {
return candidate.
source.
item == id; } );
349 isBus = claim != claims.end() && bool( claim->schema );
358 else if( !isBus && !net )
374 wxASSERT( wxThread::IsMain() );
375 std::vector<BUS_BUS_CONFLICT>
result;
376 std::map<INST_ID, std::map<KIID, VECTOR2I>> positions;
379 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
381 if( !reported.contains( key ) )
384 const auto& record =
m_records.Records().Entries().at( key )->value;
386 if( record.atoms.mixedBusShapes )
388 const auto first = std::ranges::find_if( record.claims,
389 [](
const CLAIM& claim ) { return bool( claim.schema ); } );
390 const auto second = std::find_if( first, record.claims.end(),
391 [&](
const CLAIM& claim )
393 return claim.schema && claim.schema->shape != first->schema->shape;
395 wxCHECK2( first != record.claims.end() && second != record.claims.end(),
continue );
396 const auto& points = cached( positions, key.inst,
397 [&] { return itemPositions( m_inputs.InstanceScreenFacts( key.inst ) ); } );
399 result.push_back( {
m_keys.Instance( key.inst ), first->source.item, second->source.item,
400 points.at( first->source.item ),
true } );
404 if( record.kind !=
KIND::BUNDLE || record.claims.size() < 2 )
407 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
408 const CLAIM* canonical =
nullptr;
410 std::set<wxString> leaves;
412 for(
const CLAIM& claim : record.claims )
416 if( !fact || fact->
ports.empty() || !isLabelOrSheetPin( fact->
type ) )
422 position = fact->
ports.front().position;
424 for(
const auto& leaf : claim.
schema->leaves )
425 leaves.insert( leaf.name );
430 if( std::ranges::any_of( claim.
schema->leaves,
431 [&](
const BUS_SCHEMA::LEAF& leaf ) { return leaves.contains( leaf.name ); } ) )
446 wxASSERT( wxThread::IsMain() );
447 std::vector<BUS_ENTRY_CONFLICT>
result;
449 const auto& islandOf =
m_published.Auxiliary().IslandOf();
453 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
455 if( !reported.contains( key ) )
459 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
461 for(
const auto& [entry, bus] : island.value.busEntryLinks )
464 if( islandOf.at( { entry, key.inst } ) != key || entry == previous )
467 const ITEM_FACT* busFact = findFact( facts, bus );
476 const auto signalRow = rows.find( { entry, key.inst } );
477 const auto busRow = rows.find( { bus, key.inst } );
479 if( signalRow == rows.end() || busRow == rows.end()
485 const auto& signal = *
m_published.Components().at( signalRow->second.component ).content;
486 const auto& bundle = *
m_published.Components().at( busRow->second.component ).content;
492 const bool member = std::ranges::any_of( bundle.members,
495 return m_published.SlotComponents().at( slot ) == signalRow->second.component;
501 const ITEM_FACT* fact = findFact( facts, entry );
506 result.push_back( {
m_keys.Instance( key.inst ), entry, bus,
name( signalRow->second.name ),
507 name( busRow->second.name ), fact->
ports.front().position } );
516 wxASSERT( wxThread::IsMain() );
520 std::set<KIID> others;
522 std::map<INST_ID, std::map<KIID, NC_SOURCE>> sources;
525 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
527 if( island.value.ncContacts.empty() )
530 const auto& items = cached( sources, key.inst,
531 [&] { return noConnectSources( m_inputs.InstanceScreenFacts( key.inst ) ); } );
533 for(
const auto& [first, second] : island.value.ncContacts )
535 const NC_SOURCE& a = items.at( first );
536 const NC_SOURCE& b = items.at( second );
538 if( a.nc == b.nc || a.powerFlag || b.powerFlag )
541 const NC_SOURCE&
pin = a.nc ? a : b;
542 const NC_SOURCE& other = a.nc ? b : a;
547 auto&
group = groups[{
m_keys.Instance( key.inst ),
pin.position }];
548 group.pins.insert( a.nc ? first : second );
549 group.others.insert( a.nc ? second : first );
553 std::vector<NO_CONNECT_PIN_CONFLICT>
result;
555 for(
const auto& [key,
group] : groups )
558 {
group.others.begin(),
group.others.end() }, key.second } );
566 wxASSERT( wxThread::IsMain() );
572 std::map<INST_ID, std::map<KIID, NC_SOURCE>> sources;
573 std::map<NODE_ID, NET> nets;
574 const auto source = [&](
INST_ID inst,
const KIID& id ) ->
const NC_SOURCE&
576 return cached( sources, inst, [&] {
return noConnectSources(
m_inputs.InstanceScreenFacts( inst ) ); } )
579 const auto pinWitness = [](
const NC_SOURCE& item ) {
return item.pin && !item.powerFlag; };
580 const auto& islands =
m_published.Auxiliary().Islands();
582 std::vector<NO_CONNECT_FLAG_ERROR>
result;
584 for(
const auto& [key, island] : islands )
586 if( !island.value.atoms.noConnect || island.value.atoms.busNoConnect || !reported.contains( key ) )
589 const KIID&
flag = *island.value.atoms.noConnect;
590 const VECTOR2I position = source( key.inst,
flag ).position;
591 bool hierarchy =
false;
593 bool attached =
false;
596 for(
const KIID&
id : island.value.items )
598 const NC_SOURCE& item = source( key.inst,
id );
601 attached |= hier && item.position == position;
609 for(
const auto& [first, second] : island.value.ncContacts )
611 if( first ==
flag || second ==
flag )
612 attached |= source( key.inst, first ==
flag ? second : first ).nc;
615 if( attached || ( hierarchy && clean ) )
619 const NET& net = cached( nets, row.component, [&]
622 const auto& component = *m_published.Components().at( row.component ).content;
624 for( const RECORD_KEY& record : component.records )
625 value.pins += islands.at( record ).value.atoms.pinCount;
627 for( const ITEM_KEY& id : component.items )
628 value.label |= isLabel( source( id.inst, id.item ).type );
633 const bool connected = net.pins > 1;
635 if( connected || ( net.pins == 0 && !net.label ) )
638 connected &&
pin !=
niluuid ? source( key.inst,
pin ).position : position,
648 wxASSERT( wxThread::IsMain() );
657 bool PowerSymbol()
const {
return pin && (
pin->globalPowerParent ||
pin->localPowerParent ); }
659 bool Label()
const {
return isLabelOrSheetPin( type ); }
661 const auto samePin = [](
const SOURCE& a,
const SOURCE& b )
663 if( a.group == b.group )
666 if( !a.pin || !b.pin || a.owner != b.owner || a.pin->position != b.pin->position
667 || a.pin->shownName != b.pin->shownName )
674 return a.pin->type == b.pin->type || passive( a.pin->type ) || passive( b.pin->type );
676 std::map<INST_ID, std::map<KIID, SOURCE>> sources;
677 std::map<NODE_ID, std::vector<CLAIM>> drivers;
678 std::map<NODE_ID, bool> powerConnections;
681 return cached( sources, inst, [&]
683 std::map<KIID, SOURCE> items;
688 items.emplace(
pin.id,
SOURCE{ &pin, SCH_PIN_T, fact.owner, fact.id } );
690 if( !fact.ports.empty() )
692 items.emplace( fact.id,
693 SOURCE{ nullptr, fact.type, fact.owner, fact.id, fact.ports.front().kind } );
700 const auto candidates = [&](
NODE_ID component ) ->
const std::vector<CLAIM>&
702 return cached( drivers, component, [&] {
return DriverCandidates( component ); } );
705 const auto busNoConnect = busNoConnectMembers(
m_published );
708 std::vector<UNCONNECTED_PIN>
result;
710 for(
const auto& [key, island] : auxiliary.Islands() )
712 if( island.value.atoms.noConnect || !island.value.atoms.hasSymbolPin || !reported.contains( key ) )
715 const auto& row =
m_published.Rows().at( { island.value.items.front(), key.inst } );
717 if( busNoConnect.contains( { row.component, key.inst } ) )
720 std::vector<const SOURCE*> pins;
721 bool connected =
false;
723 for(
const KIID&
id : island.value.items )
725 const SOURCE& item = source( key.inst,
id );
726 connected |= item.Label();
728 if( item.pin && !item.PowerFlag() )
729 pins.push_back( &item );
732 const SOURCE* selected =
nullptr;
734 for(
const SOURCE* item : pins )
741 if( !item->PowerSymbol() )
743 if( !selected || ( selected->pin->invisible && !
pin.invisible )
744 || (
pin.invisible == selected->pin->invisible
752 bool attached =
false;
753 const auto contact = [&](
const KIID& otherId )
755 const SOURCE& other = source( key.inst, otherId );
758 return !other.PowerFlag() && other.owner != item->owner;
765 if( island.value.dangling.at(
pin.id ) == 0 )
767 if(
const auto found = auxiliary.NeighborsOf().find( { pin.id, key.inst } );
768 found != auxiliary.NeighborsOf().end() )
769 attached = std::ranges::any_of( found->second, contact );
771 for(
const auto& [first, second] : island.value.ncContacts )
773 if( first ==
pin.id || second ==
pin.id )
774 attached |= contact( first ==
pin.id ? second : first );
778 const bool electrical = cached( powerConnections, row.component, [&]
781 const auto& component = *m_published.Components().at( row.component ).content;
783 for( const ITEM_KEY& member : component.items )
785 const SOURCE& other = source( member.inst, member.item );
786 value |= other.Label() || ( other.pin && !other.PowerSymbol() );
789 for(
const CLAIM& claim : candidates( row.component ) )
790 value |= source( claim.source.inst, claim.source.item ).Label();
795 if( !attached || !electrical )
799 if( !selected || connected )
802 for(
size_t first = 0; first < pins.size() && !connected; ++first )
804 if( pins[first]->PowerSymbol() )
807 connected = std::any_of( pins.begin() + first + 1, pins.end(),
808 [&](
const SOURCE* b ) { return !b->PowerSymbol() && !samePin( *pins[first], *b ); } );
813 connected = std::ranges::any_of( candidates( row.component ),
814 [&](
const CLAIM& claim )
816 if( claim.priority < PRIORITY::HIER_LABEL )
819 const SOURCE& driver = source( claim.source.inst, claim.source.item );
820 return !driver.PowerFlag()
821 && ( claim.source.inst != key.inst || !samePin( driver, *selected ) );
826 result.push_back( {
m_keys.Instance( key.inst ), selected->pin->id, selected->pin->position } );
834 wxASSERT( wxThread::IsMain() );
837 for(
const auto& [key, neighbors] :
m_published.Auxiliary().NeighborsOf() )
839 const auto& facts =
m_inputs.InstanceScreenFacts( key.inst ).items;
840 const ITEM_FACT* label = findFact( facts, key.item );
842 if( !label || !isLabel( label->
type ) || label->
ports.empty() )
846 std::vector<KIID> wires;
848 for(
const KIID&
id : neighbors )
850 const ITEM_FACT* wire = findFact( facts,
id );
855 wires.push_back(
id );
859 if( wires.size() < 2 )
862 std::sort( wires.begin(), wires.end() );
864 auto [entry, inserted] = locations.try_emplace( std::pair{ sheet, position },
867 if( !inserted && key.item < entry->second.label )
868 entry->second.label = key.item;
871 return moveValues( locations );
876 wxASSERT( wxThread::IsMain() );
879 std::map<KIID, const PIN_FACT*> pins;
880 std::vector<KIID> lines;
881 std::vector<KIID> members;
883 std::vector<FOUR_WAY_JUNCTION>
result;
887 const auto* instance =
m_inputs.FindInstance( inst );
890 throw std::logic_error(
"Missing instance inputs in four-way connectivity query" );
892 const auto& units = instance->value.units;
893 std::map<VECTOR2I, CONTACTS> locations;
899 const auto unit = std::lower_bound( units.begin(), units.end(), fact.owner,
900 [](
const auto& entry,
const KIID& owner ) { return entry.first < owner; } );
902 if( unit == units.end() || unit->first != fact.owner )
903 throw std::invalid_argument(
"Missing symbol unit in connectivity geometry" );
907 if(
pin.unit != 0 && unit->second != 0 &&
pin.unit != unit->second )
910 auto& contacts = locations[
pin.position];
911 contacts.members.push_back(
pin.id );
912 auto& selected = contacts.pins[fact.owner];
914 if( !selected || std::tie(
pin.invisible,
pin.id )
915 < std::tie( selected->invisible, selected->id ) )
921 for(
const PORT_FACT& port : fact.ports )
923 auto& contacts = locations[port.
position];
924 contacts.lines.push_back( fact.id );
925 contacts.members.push_back( fact.id );
930 for(
auto& [position, contacts] : locations )
932 if( contacts.pins.size() + contacts.lines.size() < 4 )
935 std::sort( contacts.members.begin(), contacts.members.end() );
936 contacts.members.erase( std::unique( contacts.members.begin(), contacts.members.end() ),
937 contacts.members.end() );
940 for(
const auto& [owner,
pin] : contacts.pins )
941 junction.
items.push_back(
pin->id );
943 std::sort( junction.items.begin(), junction.items.end() );
944 std::sort( contacts.lines.begin(), contacts.lines.end() );
945 junction.items.insert( junction.items.end(), contacts.lines.begin(), contacts.lines.end() );
946 result.push_back( std::move( junction ) );
950 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.position.x, e.position.y ); } );
956 wxASSERT( wxThread::IsMain() );
957 std::vector<NETCLASS_REFERENCE>
result;
959 for(
const auto& [instance, entry] :
m_inputs.Instances().Entries() )
960 std::ranges::copy( entry->value.netclassReferences, std::back_inserter(
result ) );
962 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.item, e.name ); } );
968 wxASSERT( wxThread::IsMain() );
969 std::vector<UNMAPPED_PIN_CANDIDATE>
result;
974 const auto* entry =
m_inputs.FindInstance( inst );
976 for(
const auto&
pin : entry->value.pinMapCandidates )
978 const auto island = auxiliary.IslandOf().find( {
pin.id, inst } );
980 if( island == auxiliary.IslandOf().end() )
983 if( !
pin.ignoresDangling && auxiliary.Islands().at( island->second ).value.dangling.at(
pin.id ) )
995 wxASSERT( wxThread::IsMain() );
996 const auto* screen =
m_inputs.FindScreen( aScreen );
997 return screen ? screen->value.pinMaps : std::vector<PIN_MAP_FACT>();
1002 wxASSERT( wxThread::IsMain() );
1003 const auto id =
m_keys.FindInstance( aPath );
1004 const auto* entry =
id ?
m_inputs.Instances().Find( *
id ) :
nullptr;
1005 return entry ?
m_inputs.InstanceScreenFacts( *id ).LibrarySymbols()
1006 : std::vector<LIBRARY_SYMBOL_FACT>();
1011 wxASSERT( wxThread::IsMain() );
1012 const auto id =
m_keys.FindInstance( aPath );
1013 const auto* entry =
id ?
m_inputs.Instances().Find( *
id ) :
nullptr;
1014 return entry ? entry->value.variantSymbols : std::vector<VARIANT_SYMBOL_FACT>();
1019 wxASSERT( wxThread::IsMain() );
1020 std::vector<FOOTPRINT_SOURCE>
result;
1024 const auto* entry =
m_inputs.FindInstance(
id );
1025 const auto& sources =
m_inputs.InstanceScreenFacts(
id ).footprints;
1026 const auto& footprints = entry->value.footprints;
1029 if( sources.size() != footprints.size() )
1030 throw std::runtime_error(
"Footprint instance facts do not match screen facts" );
1032 for(
size_t i = 0; i < footprints.size(); ++i )
1034 const auto& source = sources[i];
1035 const auto& [item, footprint] = footprints[i];
1037 if( source.id != item )
1038 throw std::runtime_error(
"Footprint instance facts do not match screen facts" );
1040 result.push_back( { sheet, item, source.position, footprint, source.filters } );
1049 wxASSERT( wxThread::IsMain() );
1050 std::map<wxString, MULTI_UNIT_GROUP> groups;
1054 std::map<KIID, const MULTI_UNIT_FACT*> sources;
1057 sources.emplace( source.id, &source );
1061 if( reference.reference.IsEmpty() || reference.reference.EndsWith(
"?" ) )
1064 const auto& source = *sources.at( reference.id );
1065 auto [
group, inserted] = groups.try_emplace( reference.reference );
1069 group->second.reference = reference.reference;
1070 group->second.units = source.units;
1073 group->second.instances.push_back( {
m_keys.Instance(
id ), source.id, source.position,
1074 reference.name, reference.footprint, reference.unit } );
1078 return moveValues( groups );
1083 wxASSERT( wxThread::IsMain() );
1084 std::vector<DUPLICATE_SHEET_ERROR>
result;
1086 for(
const auto& [instance, entry] :
m_inputs.Instances().Entries() )
1088 const auto& children = entry->value.childSheets;
1090 for(
size_t i = 0; i < children.size(); ++i )
1092 for(
size_t j = i + 1; j < children.size(); ++j )
1094 if( children[i].
name.IsSameAs( children[j].name,
false ) )
1096 result.push_back( {
m_keys.Instance( instance ), children[i].id,
1097 children[j].id, children[i].position } );
1103 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.main, e.auxiliary ); } );
1109 wxASSERT( wxThread::IsMain() );
1110 std::vector<FIELD_NAME_ERROR>
result;
1112 for(
const auto& [instance, entry] :
m_inputs.Instances().Entries() )
1115 result.push_back( {
m_keys.Instance( instance ), field } );
1118 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.field.owner, e.field.field ); } );
1124 wxASSERT( wxThread::IsMain() );
1125 std::vector<SOURCE_LOCATION>
result;
1131 if( !isLabel( fact.type ) )
1134 wxString
text = fact.rawText;
1136 if(
text.Trim(
false ).Trim(
true ).IsEmpty() )
1137 result.push_back( {
m_keys.Instance( instance ), fact.id, fact.ports.front().position } );
1146 wxASSERT( wxThread::IsMain() );
1147 std::vector<SOURCE_LOCATION>
result;
1159 if( !
text || !
text->value.canDrive || !
pin.globalPower || !
pin.invisible
1161 ||
pin.globalPowerParent ||
pin.localPowerParent )
1164 const auto& island = auxiliary.Islands().at( auxiliary.IslandOf().at( key ) );
1166 if( island.value.atoms.invisiblePowerWired )
1180 wxASSERT( wxThread::IsMain() );
1181 std::vector<SOURCE_LOCATION>
result;
1189 if( !
m_inputs.Text( { pin.id, instance } ) )
1206 wxASSERT( wxThread::IsMain() );
1207 std::vector<OFF_GRID_ENDPOINT>
result;
1208 const auto offGrid = [aGrid](
const VECTOR2I& point )
1210 return point.x % aGrid != 0 || point.y % aGrid != 0;
1213 std::set<SCREEN_ID> screens;
1218 if( !screens.insert(
m_inputs.InstanceScreen( instance ) ).second )
1222 std::map<KIID, OFF_GRID_ENDPOINT> symbols;
1226 if( fact.type ==
SCH_LINE_T && fact.segment )
1228 if( offGrid( fact.segment->A ) )
1229 result.push_back( {
path, fact.id, fact.segment->A, {} } );
1230 else if( offGrid( fact.segment->B ) )
1231 result.push_back( {
path, fact.id, fact.segment->B, {} } );
1235 for(
const PORT_FACT& port : fact.ports )
1245 || !offGrid(
pin.position ) )
1248 auto [found, inserted] = symbols.try_emplace( fact.owner );
1251 if( inserted ||
pin.id < diagnostic.
item )
1262 std::ranges::move( std::views::values( symbols ), std::back_inserter(
result ) );
1270 wxASSERT( wxThread::IsMain() );
1271 const auto isGround = [](
const wxString&
name )
1273 const wxString upper =
name.Upper();
1274 return upper.Contains( wxS(
"GND" ) ) || upper == wxS(
"EARTH" ) || upper.StartsWith( wxS(
"EARTH_" ) )
1275 || upper == wxS(
"VSS" ) || upper == wxS(
"VSSA" );
1279 bool hasGroundNet =
false;
1280 std::vector<GROUND_PIN_ERROR> mismatched;
1282 std::vector<GROUND_PIN_ERROR>
result;
1286 std::map<KIID, SYMBOL_PINS> symbols;
1293 || !
m_inputs.Text( { pin.id, inst } ) )
1301 const auto& component =
m_published.Components().at( row->second.component );
1307 if( component.content->best && component.content->best->path !=
INVALID_ID )
1308 name =
name.Mid(
m_keys.Name( component.content->best->path ).length() );
1312 auto& symbol = symbols[fact.owner];
1313 const bool ground = isGround(
name );
1314 symbol.hasGroundNet |= ground;
1316 if( isGround(
pin.shownName ) && !ground )
1317 symbol.mismatched.push_back( {
m_keys.Instance( inst ),
pin.id,
pin.position,
pin.shownName } );
1321 for(
auto& [owner, symbol] : symbols )
1323 if( symbol.hasGroundNet )
1324 std::ranges::move( symbol.mismatched, std::back_inserter(
result ) );
1328 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.pin ); } );
1334 wxASSERT( wxThread::IsMain() );
1338 bool noConnect =
false;
1339 std::map<INST_ID, size_t> slots;
1340 size_t slotCount = 0;
1342 std::map<INST_ID, size_t> localPins;
1343 std::map<INST_ID, size_t> hierarchy;
1344 bool busMember =
false;
1346 std::map<NODE_ID, CONNECTIONS> connections;
1348 const auto busNoConnect = busNoConnectMembers(
m_published );
1349 std::map<SCREEN_ID, std::set<KIID>> screenPinIds;
1351 const auto screenPins = [&](
INST_ID aInstance ) ->
const std::set<KIID>&
1353 return cached( screenPinIds,
m_inputs.InstanceScreen( aInstance ), [&]
1357 for( const ITEM_FACT& item : m_inputs.InstanceScreenFacts( aInstance ).items )
1359 for( const PIN_FACT& pin : item.pins )
1360 ids.insert( pin.id );
1369 return cached( connections,
id, [&]
1372 const auto& component = *m_published.Components().at(
id ).content;
1373 std::set<std::pair<INST_ID, wxString>> ports;
1374 std::set<std::pair<INST_ID, wxString>> hierLabels;
1375 net.busMember = !component.slots.empty();
1377 for(
const RECORD_KEY& key : component.records )
1379 const auto& island = auxiliary.Islands().at( key ).value;
1380 const auto& facts = m_inputs.InstanceScreenFacts( key.
inst ).items;
1381 const auto& pins = screenPins( key.
inst );
1382 net.count += island.atoms.pinCount;
1383 net.noConnect |= island.atoms.noConnect.has_value();
1385 for(
const KIID& item : island.items )
1388 if( pins.contains( item ) )
1391 ++net.localPins[key.
inst];
1395 const ITEM_FACT* source = findFact( facts, item );
1403 ++net.hierarchy[key.
inst];
1407 const wxString&
name = m_inputs.Text( { item, key.
inst } )->value.name;
1408 hierLabels.emplace( key.
inst,
name );
1410 if( m_keys.Instance( key.
inst ).size() > 1 )
1416 net.count += ports.size();
1418 for(
const auto& [instance,
name] : hierLabels )
1419 ++net.hierarchy[instance];
1421 std::set<NAME_KEY, KEY_LESS> neighbors( KEY_LESS{ m_keys } );
1423 for(
const SLOT_KEY& slot : component.slots )
1425 for(
const NAME_KEY& edge : m_slots.Slots().Entries().at( slot )->value.edges )
1427 if( edge.scope == SCOPE::SHEET )
1428 neighbors.insert( edge );
1432 for(
const NAME_KEY& neighbor : neighbors )
1434 ++net.slots[neighbor.inst];
1441 const auto reported = ercIslands();
1442 std::vector<LABEL_CONNECTION_ERROR>
result;
1444 for(
const auto& [key, island] : auxiliary.Islands() )
1446 if( !reported.contains( key ) )
1449 const auto& facts = m_inputs.InstanceScreenFacts( key.
inst ).items;
1451 for(
const KIID&
id : island.value.items )
1453 const ITEM_FACT* label = findFact( facts,
id );
1455 if( !label || !isLabel( label->type ) )
1458 const auto& row = m_published.Rows().at( { id, key.
inst } );
1460 if( row.kind == KIND::BUNDLE )
1463 const CONNECTIONS& net = netConnections( row.component );
1464 const auto localSlots = net.slots.find( key.
inst );
1465 const size_t count = net.count + net.slotCount
1466 - ( localSlots == net.slots.end() ? 0 : localSlots->second );
1467 const auto hierarchy = net.hierarchy.find( key.
inst );
1468 const bool busNc = busNoConnect.contains( { row.component, key.
inst } );
1471 const bool routesHierarchy = hierarchy != net.hierarchy.end()
1472 && hierarchy->second > ( net.busMember ? 0U : 1U );
1473 const bool orphan = label->type ==
SCH_LABEL_T && !net.localPins.contains( key.
inst ) && net.pins > 1
1474 && !net.noConnect && !busNc && !routesHierarchy;
1475 const bool unconnected = island.value.dangling.at(
id ) != 0 || count == 0 || orphan;
1476 const bool singlePin = count == 1 && !net.noConnect && !busNc;
1479 result.push_back( { m_keys.Instance( key.
inst ), id, label->ports.front().position,
false } );
1482 result.push_back( { m_keys.Instance( key.
inst ), id, label->ports.front().position,
true } );
1491 wxASSERT( wxThread::IsMain() );
1492 std::vector<LABEL_LOCATION>
result;
1494 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
1496 const auto& facts =
m_inputs.InstanceScreenFacts( key.
inst ).items;
1498 for(
const auto& [
id, dangling] : island.value.dangling )
1500 const ITEM_FACT* fact = dangling ? findFact( facts,
id ) :
nullptr;
1512 wxASSERT( wxThread::IsMain() );
1513 std::map<NAME_ID, ERC_PIN_NET, NAME_LESS> nets(
NAME_LESS{ &
m_keys } );
1522 auto [it, inserted] = nets.try_emplace( row.name );
1525 it->second.name =
m_keys.Name( row.name );
1536 if(
auto* net = netFor( { fact.id, inst } ) )
1537 net->noConnect =
true;
1543 auto* net =
text ? netFor( {
pin.id, inst } ) :
nullptr;
1550 net->pins.push_back( {
m_keys.Instance( inst ), fact.owner,
text->value.reference,
pin } );
1555 return moveValues( nets );
1560 wxASSERT( wxThread::IsMain() );
1566 std::map<std::pair<std::string, std::string>, std::vector<PIN_ON_NET>> groups;
1572 if( !fact.multiUnit )
1587 groups[{
text->value.reference.utf8_string(),
pin.shownNumber.utf8_string() }].push_back(
1588 { {
m_keys.Instance( inst ),
pin.id,
pin.position }, row.name } );
1593 std::vector<MULTI_UNIT_CONFLICT>
result;
1595 for(
auto& [key, pins] : groups )
1597 std::ranges::sort( pins, [&](
const PIN_ON_NET& a,
const PIN_ON_NET& b )
1599 if( a.net != b.net )
1600 return m_keys.NameLess( a.net, b.net );
1602 if( a.location.item != b.location.item )
1603 return a.location.item < b.location.item;
1606 return a.location.sheet < b.location.sheet;
1608 const PIN_ON_NET& first = pins.front();
1609 const auto other = std::ranges::find_if( pins, [&](
const PIN_ON_NET&
pin ) {
return pin.net != first.net; } );
1611 if( other != pins.end() )
1613 result.push_back( { first.location, other->location, wxString::FromUTF8( key.second ),
1614 m_keys.Name( first.net ),
m_keys.Name( other->net ) } );
1623 wxASSERT( wxThread::IsMain() );
1624 std::vector<NAMED_ITEM>
result;
1625 const auto unescapeNetName = []( wxString
name )
1628 name.Replace( wxS(
"{slash}" ), wxS(
"/" ) );
1638 if( isLabel( fact.type ) )
1641 result.push_back( {
path, fact.id, fact.ports.front().position,
1647 if( !
pin.globalPower && !
pin.localPower )
1651 if(
const auto*
text =
m_inputs.Text( { pin.id, inst } ) )
1665 wxASSERT( wxThread::IsMain() );
1666 std::map<std::string, std::optional<LABEL_LOCATION>> labels;
1675 const wxString&
name =
m_inputs.Text( { fact.id, inst } )->value.name;
1676 auto [entry, inserted] = labels.try_emplace(
name.utf8_string(),
1677 LABEL_LOCATION{ m_keys.Instance( inst ), fact.id, fact.ports.front().position } );
1680 entry->second.reset();
1684 std::vector<LABEL_LOCATION>
result;
1686 for(
const auto& [
name, label] : labels )
1689 result.push_back( *label );
1697 wxASSERT( wxThread::IsMain() );
1701 std::map<wxString, std::vector<const ITEM_FACT*>> labels;
1702 std::map<std::pair<KIID, wxString>, std::vector<const ITEM_FACT*>> pins;
1704 std::map<INST_ID, PORTS> instances;
1705 std::vector<HIERARCHY_ERROR>
result;
1708 for(
INST_ID inst : islandInstances( auxiliary.Islands() ) )
1710 PORTS& ports = instances[inst];
1717 const wxString&
name =
m_inputs.Text( { fact.id, inst } )->value.name;
1721 ports.labels[
name].push_back( &fact );
1725 ports.pins[{ fact.owner,
name }].push_back( &fact );
1726 const auto& record = auxiliary.IslandOf().at( { fact.id, inst } );
1728 if( auxiliary.Islands().at( record ).value.dangling.at( fact.id ) != 0 )
1730 result.push_back( {
m_keys.Instance( inst ), fact.id, fact.ports.front().position,
1731 name, ERROR_KIND::DANGLING_PIN, {} } );
1737 const auto mismatch = [&](
const KIID_PATH&
path,
const std::vector<const ITEM_FACT*>& items,
1738 const wxString&
name, ERROR_KIND kind )
1740 const ITEM_FACT& first = *items.front();
1746 result.push_back( std::move( error ) );
1749 for(
const auto& [inst, ports] : instances )
1753 const KIID owner = parentPath.back();
1754 parentPath.pop_back();
1755 const auto parent =
m_keys.FindInstance( parentPath );
1756 const bool hasParent = parent &&
m_inputs.FindInstance( *parent );
1758 const auto parentPorts = hasParent ? instances.find( *parent ) : instances.end();
1760 for(
const auto& [
name, labels] : ports.labels )
1766 result.push_back( {
path, label->id, label->ports.front().position,
name,
1767 ERROR_KIND::ROOT_LABEL, {} } );
1770 else if( parentPorts == instances.end() || !parentPorts->second.pins.contains( { owner, name } ) )
1772 mismatch(
path, labels,
name, ERROR_KIND::MISSING_PIN );
1776 for(
const auto& [key, pins] : ports.pins )
1779 childPath.push_back( key.first );
1780 const auto child =
m_keys.FindInstance( childPath );
1781 const auto found = child ? instances.find( *child ) : instances.end();
1783 if( found == instances.end() || !found->second.labels.contains( key.second ) )
1784 mismatch(
path, pins, key.second, ERROR_KIND::MISSING_LABEL );
1788 std::ranges::sort(
result, [](
const auto& a,
const auto& b )
1790 if( a.sheet != b.sheet )
1791 return a.sheet < b.sheet;
1794 return std::tie( a.item, a.kind ) < std::tie( b.item, b.kind );
std::map< RECORD_KEY, ISLAND_ENTRY, KEY_LESS > ISLANDS
std::vector< NO_CONNECT_PIN_CONFLICT > NoConnectPinConflicts() const
No-connect pins that touch other items, grouped by instance and position.
std::vector< MULTI_UNIT_GROUP > MultiUnitSymbols() const
Annotated multi-unit symbols grouped by reference, with instances in page order.
std::vector< ERC_PIN_NET > PinNets() const
Active pins for each named net, in net name order.
std::vector< NO_CONNECT_FLAG_ERROR > NoConnectFlagErrors() const
No-connect flags on reported islands that connect to many pins or to nothing.
std::vector< OFF_GRID_ENDPOINT > OffGridEndpoints(int aGrid) const
Off-grid endpoints on the first instance of each screen; one pin for each symbol.
std::vector< LABEL_LOCATION > SingleGlobalLabels() const
Global labels whose name occurs only once in the hierarchy.
std::vector< DUPLICATE_SHEET_ERROR > DuplicateSheetNames() const
Child sheet pairs whose names differ only in case, sorted by instance and pair.
std::vector< GROUND_PIN_ERROR > GroundPinErrors() const
Ground-named power pins off a ground net, sorted by instance and pin.
std::vector< PIN_MAP_FACT > PinMapSymbols(SCREEN_ID aScreen) const
Captured pin maps of one screen; empty for an unknown screen.
std::vector< BUS_BUS_CONFLICT > BusBusConflicts() const
Reported bus islands with mixed bus shapes, or with two claims that share no member.
std::vector< BUS_ENTRY_CONFLICT > BusEntryConflicts() const
Bus wire entries on reported islands whose net is not a member of the touched bus.
std::vector< UNCONNECTED_PIN > UnconnectedPins() const
Unconnected symbol pins on reported islands; each power symbol pin reports on its own.
std::vector< DRIVER_CONFLICT > DriverConflicts() const
Islands with two different strong names; one result for each reported island.
std::vector< WIRE_ENDPOINT > DanglingWireEndpoints() const
Dangling ends of wires and bus wire entries on reported islands.
std::vector< MULTI_UNIT_CONFLICT > MultiUnitPinConflicts() const
One conflict for each multi-unit reference and pin number with more than one net.
std::vector< VARIANT_SYMBOL_FACT > VariantSymbols(const KIID_PATH &aPath) const
Captured variant symbol overrides of one instance; empty for an unknown path.
std::vector< NAMED_ITEM > NamedItems() const
Every label and power pin with its resolved name, on every instance.
std::set< RECORD_KEY, KEY_LESS > ercIslands() const
Islands that ERC reports.
std::vector< FOUR_WAY_JUNCTION > FourWayJunctions() const
Positions with four or more contacts, sorted by instance and position.
std::vector< BUS_NET_CONFLICT > BusNetConflicts() const
Reported islands whose ERC_ATOMS::kindConflict is set and that hold both a net and a bus item.
std::vector< FOOTPRINT_SOURCE > FootprintSources() const
Resolved footprint and filters of every symbol on every instance.
std::vector< LIBRARY_SYMBOL_FACT > LibrarySymbols(const KIID_PATH &aPath) const
Captured library links of the symbols on the screen of one instance; empty for an unknown path.
std::vector< INST_ID > instancesInPageOrder() const
All captured instances, sorted by page order and then by instance path.
std::vector< NETCLASS_REFERENCE > NetclassReferences() const
Netclass field references of every instance, sorted by instance, item and name.
std::vector< LABEL_LOCATION > DanglingDirectives() const
Dangling directive labels on every instance.
std::vector< FLOATING_WIRE > FloatingWires() const
Wires and bus entries of each island whose net has no driver, on every instance.
std::vector< HIERARCHY_ERROR > HierarchyErrors() const
Hierarchical label and sheet pin mismatches, sorted by instance, item and kind.
std::vector< SOURCE_LOCATION > InvalidPinNotation() const
Pins whose shown number looks like stacked pin notation but does not parse.
std::vector< FIELD_NAME_ERROR > InvalidFieldNames() const
Field names with leading or trailing white space, sorted by instance, owner and field.
std::vector< SOURCE_LOCATION > WiredImplicitPowerPins() const
Hidden global power input pins that a wire also joins; one pin for each symbol.
std::vector< SOURCE_LOCATION > EmptyLabels() const
Labels whose raw text is empty or only white space, on every instance.
std::vector< LABEL_CONNECTION_ERROR > LabelConnectionErrors() const
Unconnected and single-pin labels on reported islands.
std::vector< UNMAPPED_PIN_CANDIDATE > UnmappedPinCandidates() const
Pins that need a pad check.
std::vector< LABEL_WIRE_CONFLICT > LabelWireConflicts() const
Labels that touch the interior of two or more wires; one result for each position.
std::vector< CLAIM > DriverCandidates(NODE_ID aComponent) const
Owned eligible claims of one published component, strongest first.
Value keys and the key session of the schematic connectivity engine.
uint32_t INST_ID
Session handle of a sheet instance KIID_PATH.
uint32_t NAME_ID
Session handle of a name, ordered by UTF-8 value through NAME_LESS.
uint64_t SCREEN_ID
Process-local SCH_SCREEN::ConnectivityId(), never a file UUID.
SOURCE_LOCATION LABEL_LOCATION
uint32_t NODE_ID
Session handle of a NODE_KEY graph node.
constexpr uint32_t INVALID_ID
Marks an unset handle.
@ GLOBAL_POWER_PIN
Power input pin of a global power symbol, or a hidden power input pin.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
@ PT_NC
not connected (must be left open)
@ PT_NIC
not internally connected (may be connected to anything)
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
@ NET
This item represents a net.
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
The claim of one item for the name of its island.
ITEM_KEY source
The claiming item, the last tie-break.
bool Strong() const
True for a name that the user placed.
NAME_ID name
Item text without the sheet prefix.
std::shared_ptr< const BUS_SCHEMA > schema
Parsed bus, or null for a signal.
All active pins that the publication places on one named net.
Up to four wires and bus entries of one island that has no driver.
Four or more contacts at one position.
std::vector< KIID > items
One pin for each symbol, then the lines; ERC reports four.
A hierarchical label or sheet pin with no partner, or a dangling sheet pin.
std::vector< KIID > equivalentItems
For a missing partner, all same-named labels or sheet pins.
A value copy of one connectable item, or of one group of pins that share a number and a position.
KIID id
The item KIID, or the smallest member KIID of a pin group.
std::optional< SEG > segment
std::vector< PORT_FACT > ports
One item or pin in one sheet instance.
KIID item
The item or pin KIID.
Orders keys by value through SESSION_KEYS::Less().
A label that sits on the interior of two or more wires.
Orders name handles by UTF-8 value.
A wire end, a bus entry end or a symbol pin that is not on the connection grid.
std::vector< std::pair< KIID, VECTOR2I > > equivalentPins
All off-grid pins of the symbol.
One connection point of an item.
One island in one sheet instance.
INST_ID inst
The sheet instance of the record.
The value copy of one screen.
One member position of a bus.
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
VECTOR2< int32_t > VECTOR2I