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;
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;
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 ) )
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 } );
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 )
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 )
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() );
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];
1038 throw std::runtime_error(
"Footprint instance facts do not match screen facts" );
1049 wxASSERT( wxThread::IsMain() );
1050 std::map<wxString, MULTI_UNIT_GROUP> groups;
1054 std::map<KIID, const MULTI_UNIT_FACT*> sources;
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;
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;
1157 if( ( !
pin.globalPower && !
pin.localPower ) || ( !
pin.globalPowerParent && !
pin.localPowerParent ) )
1162 if(
const auto*
text =
m_inputs.Text( { pin.id, instance } ) )
1166 if(
name.Trim(
false ).Trim(
true ).IsEmpty() )
1181 wxASSERT( wxThread::IsMain() );
1182 std::vector<SOURCE_LOCATION>
result;
1194 if( !
text || !
text->value.canDrive || !
pin.globalPower || !
pin.invisible
1196 ||
pin.globalPowerParent ||
pin.localPowerParent )
1199 const auto& island = auxiliary.Islands().at( auxiliary.IslandOf().at( key ) );
1201 if( island.value.atoms.invisiblePowerWired )
1215 wxASSERT( wxThread::IsMain() );
1216 std::vector<SOURCE_LOCATION>
result;
1224 if( !
m_inputs.Text( { pin.id, instance } ) )
1241 wxASSERT( wxThread::IsMain() );
1242 std::vector<OFF_GRID_ENDPOINT>
result;
1243 const auto offGrid = [aGrid](
const VECTOR2I& point )
1245 return point.x % aGrid != 0 || point.y % aGrid != 0;
1248 std::set<SCREEN_ID> screens;
1253 if( !screens.insert(
m_inputs.InstanceScreen( instance ) ).second )
1257 std::map<KIID, OFF_GRID_ENDPOINT> symbols;
1261 if( fact.type ==
SCH_LINE_T && fact.segment )
1263 if( offGrid( fact.segment->A ) )
1264 result.push_back( {
path, fact.id, fact.segment->A, {} } );
1265 else if( offGrid( fact.segment->B ) )
1266 result.push_back( {
path, fact.id, fact.segment->B, {} } );
1270 for(
const PORT_FACT& port : fact.ports )
1280 || !offGrid(
pin.position ) )
1283 auto [found, inserted] = symbols.try_emplace( fact.owner );
1286 if( inserted ||
pin.id < diagnostic.
item )
1297 std::ranges::move( std::views::values( symbols ), std::back_inserter(
result ) );
1305 wxASSERT( wxThread::IsMain() );
1306 const auto isGround = [](
const wxString&
name )
1308 const wxString upper =
name.Upper();
1309 return upper.Contains( wxS(
"GND" ) ) || upper == wxS(
"EARTH" ) || upper.StartsWith( wxS(
"EARTH_" ) )
1310 || upper == wxS(
"VSS" ) || upper == wxS(
"VSSA" );
1314 bool hasGroundNet =
false;
1315 std::vector<GROUND_PIN_ERROR> mismatched;
1317 std::vector<GROUND_PIN_ERROR>
result;
1321 std::map<KIID, SYMBOL_PINS> symbols;
1328 || !
m_inputs.Text( { pin.id, inst } ) )
1336 const auto& component =
m_published.Components().at( row->second.component );
1342 if( component.content->best && component.content->best->path !=
INVALID_ID )
1343 name =
name.Mid(
m_keys.Name( component.content->best->path ).length() );
1347 auto& symbol = symbols[fact.owner];
1348 const bool ground = isGround(
name );
1349 symbol.hasGroundNet |= ground;
1351 if( isGround(
pin.shownName ) && !ground )
1352 symbol.mismatched.push_back( {
m_keys.Instance( inst ),
pin.id,
pin.position,
pin.shownName } );
1356 for(
auto& [owner, symbol] : symbols )
1358 if( symbol.hasGroundNet )
1359 std::ranges::move( symbol.mismatched, std::back_inserter(
result ) );
1363 std::ranges::sort(
result, {}, [](
const auto& e ) {
return std::tie( e.sheet, e.pin ); } );
1369 wxASSERT( wxThread::IsMain() );
1373 bool noConnect =
false;
1374 std::map<INST_ID, size_t> slots;
1375 size_t slotCount = 0;
1377 std::map<INST_ID, size_t> localPins;
1378 std::map<INST_ID, size_t> hierarchy;
1379 bool busMember =
false;
1381 std::map<NODE_ID, CONNECTIONS> connections;
1383 const auto busNoConnect = busNoConnectMembers(
m_published );
1384 std::map<SCREEN_ID, std::set<KIID>> screenPinIds;
1386 const auto screenPins = [&](
INST_ID aInstance ) ->
const std::set<KIID>&
1388 return cached( screenPinIds,
m_inputs.InstanceScreen( aInstance ), [&]
1392 for( const ITEM_FACT& item : m_inputs.InstanceScreenFacts( aInstance ).items )
1394 for( const PIN_FACT& pin : item.pins )
1395 ids.insert( pin.id );
1404 return cached( connections,
id, [&]
1407 const auto& component = *m_published.Components().at(
id ).content;
1408 std::set<std::pair<INST_ID, wxString>> ports;
1409 std::set<std::pair<INST_ID, wxString>> hierLabels;
1410 net.busMember = !component.slots.empty();
1412 for(
const RECORD_KEY& key : component.records )
1414 const auto& island = auxiliary.Islands().at( key ).value;
1415 const auto& facts = m_inputs.InstanceScreenFacts( key.
inst ).items;
1416 const auto& pins = screenPins( key.
inst );
1417 net.count += island.atoms.pinCount;
1418 net.noConnect |= island.atoms.noConnect.has_value();
1420 for(
const KIID& item : island.items )
1423 if( pins.contains( item ) )
1426 ++net.localPins[key.
inst];
1430 const ITEM_FACT*
source = findFact( facts, item );
1438 ++net.hierarchy[key.
inst];
1442 const wxString&
name = m_inputs.Text( { item, key.
inst } )->value.name;
1443 hierLabels.emplace( key.
inst,
name );
1445 if( m_keys.Instance( key.
inst ).size() > 1 )
1451 net.count += ports.size();
1453 for(
const auto& [instance,
name] : hierLabels )
1454 ++net.hierarchy[instance];
1456 std::set<NAME_KEY, KEY_LESS> neighbors( KEY_LESS{ m_keys } );
1458 for(
const SLOT_KEY& slot : component.slots )
1460 for(
const NAME_KEY& edge : m_slots.Slots().Entries().at( slot )->value.edges )
1462 if( edge.scope == SCOPE::SHEET )
1463 neighbors.insert( edge );
1467 for(
const NAME_KEY& neighbor : neighbors )
1469 ++net.slots[neighbor.inst];
1476 const auto reported = ercIslands();
1477 std::vector<LABEL_CONNECTION_ERROR>
result;
1479 for(
const auto& [key, island] : auxiliary.Islands() )
1481 if( !reported.contains( key ) )
1484 const auto& facts = m_inputs.InstanceScreenFacts( key.
inst ).items;
1486 for(
const KIID&
id : island.value.items )
1488 const ITEM_FACT* label = findFact( facts,
id );
1490 if( !label || !isLabel( label->type ) )
1493 const auto& row = m_published.Rows().at( { id, key.
inst } );
1495 if( row.kind == KIND::BUNDLE )
1498 const CONNECTIONS& net = netConnections( row.component );
1499 const auto localSlots = net.slots.find( key.
inst );
1500 const size_t count = net.count + net.slotCount
1501 - ( localSlots == net.slots.end() ? 0 : localSlots->second );
1502 const auto hierarchy = net.hierarchy.find( key.
inst );
1503 const bool busNc = busNoConnect.contains( { row.component, key.
inst } );
1506 const bool routesHierarchy = hierarchy != net.hierarchy.end()
1507 && hierarchy->second > ( net.busMember ? 0U : 1U );
1508 const bool orphan = label->type ==
SCH_LABEL_T && !net.localPins.contains( key.
inst ) && net.pins > 1
1509 && !net.noConnect && !busNc && !routesHierarchy;
1510 const bool unconnected = island.value.dangling.at(
id ) != 0 || count == 0 || orphan;
1511 const bool singlePin = count == 1 && !net.noConnect && !busNc;
1514 result.push_back( { m_keys.Instance( key.
inst ), id, label->ports.front().position,
false } );
1517 result.push_back( { m_keys.Instance( key.
inst ), id, label->ports.front().position,
true } );
1526 wxASSERT( wxThread::IsMain() );
1527 std::vector<LABEL_LOCATION>
result;
1529 for(
const auto& [key, island] :
m_published.Auxiliary().Islands() )
1531 const auto& facts =
m_inputs.InstanceScreenFacts( key.
inst ).items;
1533 for(
const auto& [
id, dangling] : island.value.dangling )
1535 const ITEM_FACT* fact = dangling ? findFact( facts,
id ) :
nullptr;
1547 wxASSERT( wxThread::IsMain() );
1548 std::map<NAME_ID, ERC_PIN_NET, NAME_LESS> nets(
NAME_LESS{ &
m_keys } );
1557 auto [it, inserted] = nets.try_emplace( row.name );
1560 it->second.name =
m_keys.Name( row.name );
1571 if(
auto* net = netFor( { fact.id, inst } ) )
1572 net->noConnect =
true;
1578 auto* net =
text ? netFor( {
pin.id, inst } ) :
nullptr;
1585 net->pins.push_back( {
m_keys.Instance( inst ), fact.owner,
text->value.reference,
pin } );
1590 return moveValues( nets );
1595 wxASSERT( wxThread::IsMain() );
1601 std::map<std::pair<std::string, std::string>, std::vector<PIN_ON_NET>> groups;
1607 if( !fact.multiUnit )
1622 groups[{
text->value.reference.utf8_string(),
pin.shownNumber.utf8_string() }].push_back(
1623 { {
m_keys.Instance( inst ),
pin.id,
pin.position }, row.name } );
1628 std::vector<MULTI_UNIT_CONFLICT>
result;
1630 for(
auto& [key, pins] : groups )
1632 std::ranges::sort( pins, [&](
const PIN_ON_NET& a,
const PIN_ON_NET& b )
1634 if( a.net != b.net )
1635 return m_keys.NameLess( a.net, b.net );
1637 if( a.location.item != b.location.item )
1638 return a.location.item < b.location.item;
1641 return a.location.sheet < b.location.sheet;
1643 const PIN_ON_NET& first = pins.front();
1644 const auto other = std::ranges::find_if( pins, [&](
const PIN_ON_NET&
pin ) {
return pin.net != first.net; } );
1646 if( other != pins.end() )
1648 result.push_back( { first.location, other->location, wxString::FromUTF8( key.second ),
1649 m_keys.Name( first.net ),
m_keys.Name( other->net ) } );
1658 wxASSERT( wxThread::IsMain() );
1659 std::vector<NAMED_ITEM>
result;
1660 const auto unescapeNetName = []( wxString
name )
1663 name.Replace( wxS(
"{slash}" ), wxS(
"/" ) );
1673 if( isLabel( fact.type ) )
1676 result.push_back( {
path, fact.id, fact.ports.front().position,
1682 if( !
pin.globalPower && !
pin.localPower )
1686 if(
const auto*
text =
m_inputs.Text( { pin.id, inst } ) )
1700 wxASSERT( wxThread::IsMain() );
1701 std::map<std::string, std::optional<LABEL_LOCATION>> labels;
1710 const wxString&
name =
m_inputs.Text( { fact.id, inst } )->value.name;
1711 auto [entry, inserted] = labels.try_emplace(
name.utf8_string(),
1712 LABEL_LOCATION{ m_keys.Instance( inst ), fact.id, fact.ports.front().position } );
1715 entry->second.reset();
1719 std::vector<LABEL_LOCATION>
result;
1721 for(
const auto& [
name, label] : labels )
1724 result.push_back( *label );
1732 wxASSERT( wxThread::IsMain() );
1736 std::map<wxString, std::vector<const ITEM_FACT*>> labels;
1737 std::map<std::pair<KIID, wxString>, std::vector<const ITEM_FACT*>> pins;
1739 std::map<INST_ID, PORTS> instances;
1740 std::vector<HIERARCHY_ERROR>
result;
1743 for(
INST_ID inst : islandInstances( auxiliary.Islands() ) )
1745 PORTS& ports = instances[inst];
1752 const wxString&
name =
m_inputs.Text( { fact.id, inst } )->value.name;
1756 ports.labels[
name].push_back( &fact );
1760 ports.pins[{ fact.owner,
name }].push_back( &fact );
1761 const auto& record = auxiliary.IslandOf().at( { fact.id, inst } );
1763 if( auxiliary.Islands().at( record ).value.dangling.at( fact.id ) != 0 )
1765 result.push_back( {
m_keys.Instance( inst ), fact.id, fact.ports.front().position,
1766 name, ERROR_KIND::DANGLING_PIN, {} } );
1772 const auto mismatch = [&](
const KIID_PATH&
path,
const std::vector<const ITEM_FACT*>& items,
1773 const wxString&
name, ERROR_KIND kind )
1775 const ITEM_FACT& first = *items.front();
1781 result.push_back( std::move( error ) );
1784 for(
const auto& [inst, ports] : instances )
1788 const KIID owner = parentPath.back();
1789 parentPath.pop_back();
1790 const auto parent =
m_keys.FindInstance( parentPath );
1791 const bool hasParent = parent &&
m_inputs.FindInstance( *parent );
1793 const auto parentPorts = hasParent ? instances.find( *parent ) : instances.end();
1795 for(
const auto& [
name, labels] : ports.labels )
1801 result.push_back( {
path, label->id, label->ports.front().position,
name,
1802 ERROR_KIND::ROOT_LABEL, {} } );
1805 else if( parentPorts == instances.end() || !parentPorts->second.pins.contains( { owner, name } ) )
1807 mismatch(
path, labels,
name, ERROR_KIND::MISSING_PIN );
1811 for(
const auto& [key, pins] : ports.pins )
1814 childPath.push_back( key.first );
1815 const auto child =
m_keys.FindInstance( childPath );
1816 const auto found = child ? instances.find( *child ) : instances.end();
1818 if( found == instances.end() || !found->second.labels.contains( key.second ) )
1819 mismatch(
path, pins, key.second, ERROR_KIND::MISSING_LABEL );
1823 std::ranges::sort(
result, [](
const auto& a,
const auto& b )
1825 if( a.sheet != b.sheet )
1826 return a.sheet < b.sheet;
1829 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< SOURCE_LOCATION > EmptyPowerValues() const
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.
std::vector< DATABASE_CONNECTION::ROW > rows
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
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