KiCad PCB EDA Suite
Loading...
Searching...
No Matches
import_net_reconciler.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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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
21
22#include <board.h>
23#include <footprint.h>
24#include <import_net_names.h>
25#include <netinfo.h>
27#include <pad.h>
28#include <pcb_field.h>
30#include <reporter.h>
31#include <template_fieldnames.h>
32
33#include <map>
34#include <set>
35#include <vector>
36
37
38namespace
39{
40using PIN_SET = std::set<std::pair<wxString, wxString>>;
41
42constexpr int MAX_REPORTED = 5;
43
44
45// a reference used twice, or not yet assigned, names no single footprint
46std::map<wxString, FOOTPRINT*> uniqueFootprints( BOARD& aBoard )
47{
48 std::map<wxString, FOOTPRINT*> footprints;
49 std::set<wxString> repeated;
50
51 for( FOOTPRINT* footprint : aBoard.Footprints() )
52 {
53 if( footprint->GetReference().EndsWith( wxS( "?" ) ) )
54 continue;
55
56 if( !footprints.emplace( footprint->GetReference(), footprint ).second )
57 repeated.insert( footprint->GetReference() );
58 }
59
60 for( const wxString& ref : repeated )
61 footprints.erase( ref );
62
63 return footprints;
64}
65} // namespace
66
67
68int ReconcileImportedReferences( BOARD& aBoard, NETLIST& aNetlist )
69{
70 std::set<wxString> boardRefs;
71
72 for( FOOTPRINT* footprint : aBoard.Footprints() )
73 boardRefs.insert( footprint->GetReference() );
74
75 std::set<wxString> schematicRefs;
76
77 // the schematic references no footprint carries, by upper case, with a count to spot ambiguity
78 std::map<wxString, std::pair<wxString, int>> orphans;
79
80 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
81 {
82 const wxString ref = aNetlist.GetComponent( i )->GetReference();
83
84 schematicRefs.insert( ref );
85
86 if( boardRefs.count( ref ) )
87 continue;
88
89 auto& orphan = orphans[ref.Upper()];
90 orphan.first = ref;
91 orphan.second++;
92 }
93
94 // Allegro upper cases a reference and the importer prefixes one that starts with a digit
95 std::map<wxString, std::vector<FOOTPRINT*>> claims;
96
97 for( FOOTPRINT* footprint : aBoard.Footprints() )
98 {
99 const wxString ref = footprint->GetReference();
100
101 if( ref.EndsWith( wxS( "?" ) ) || schematicRefs.count( ref ) )
102 continue;
103
104 wxString key = ref.Upper();
105
106 // the importer prefixes only a reference that starts with a non-letter
107 const bool prefixed = key.StartsWith( wxS( "UNK" ) ) && key.length() > 3 && !wxIsalpha( key[3] );
108
109 if( prefixed && orphans.count( key ) && orphans.count( key.Mid( 3 ) ) )
110 continue;
111
112 if( prefixed && !orphans.count( key ) )
113 key = key.Mid( 3 );
114
115 if( orphans.count( key ) )
116 claims[key].push_back( footprint );
117 }
118
119 int renamed = 0;
120
121 for( const auto& [key, footprints] : claims )
122 {
123 const auto& orphan = orphans.at( key );
124
125 if( orphan.second != 1 || footprints.size() != 1 )
126 continue;
127
128 footprints.front()->SetReference( orphan.first );
129 renamed++;
130 }
131
132 return renamed;
133}
134
135
137{
139
140 std::map<wxString, FOOTPRINT*> footprints = uniqueFootprints( aBoard );
141
142 // the pins of every schematic net, on the footprints the board has
143 std::map<wxString, PIN_SET> schematicNets;
144 std::set<wxString> shared;
145
146 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
147 {
148 COMPONENT* component = aNetlist.GetComponent( i );
149 wxString ref = component->GetReference();
150
151 if( !footprints.count( ref ) )
152 continue;
153
154 shared.insert( ref );
155
156 for( unsigned j = 0; j < component->GetNetCount(); j++ )
157 {
158 const COMPONENT_NET& pin = component->GetNet( j );
159
160 if( !pin.GetNetName().IsEmpty() )
161 schematicNets[pin.GetNetName()].emplace( ref, pin.GetPinName() );
162 }
163 }
164
165 std::map<PIN_SET, wxString> schematicNameByPins;
166
167 for( const auto& [name, pins] : schematicNets )
168 schematicNameByPins.emplace( pins, name );
169
170 // the pads of every board net, and the pads without one
171 std::map<wxString, PIN_SET> boardNets;
172 std::map<std::pair<wxString, wxString>, std::vector<PAD*>> bareJoins;
173
174 for( const wxString& ref : shared )
175 {
176 for( PAD* pad : footprints.at( ref )->Pads() )
177 {
178 std::pair<wxString, wxString> key( ref, pad->GetNumber() );
179
180 if( pad->GetNetCode() > 0 )
181 boardNets[pad->GetNetname()].insert( key );
182 else
183 bareJoins[key].push_back( pad );
184 }
185 }
186
187 std::map<wxString, wxString> renames;
188
189 for( const auto& [boardName, pins] : boardNets )
190 {
191 auto match = schematicNameByPins.find( pins );
192
193 if( match == schematicNameByPins.end() )
194 {
195 result.m_conflicting++;
196
197 if( result.m_conflicting <= MAX_REPORTED )
198 {
199 aReporter.Report( wxString::Format( _( "Board net '%s' does not match a single schematic net.\n" ),
200 boardName ),
202 }
203
204 continue;
205 }
206
207 if( match->second == boardName )
208 result.m_alreadyNamed++;
209 else
210 renames.emplace( boardName, match->second );
211 }
212
213 // a rename onto a name a board net keeps would reject the whole batch, and dropping one such rename can
214 // leave the name that another rename was moving onto occupied
215 for( bool removed = true; removed; )
216 {
217 removed = false;
218
219 for( auto it = renames.begin(); it != renames.end(); )
220 {
221 if( aBoard.FindNet( it->second ) && !renames.count( it->second ) )
222 {
223 result.m_conflicting++;
224 it = renames.erase( it );
225 removed = true;
226 }
227 else
228 {
229 ++it;
230 }
231 }
232 }
233
234 if( ApplyImportedNetNameMap( aBoard, renames, aReporter ) )
235 result.m_renamed = static_cast<int>( renames.size() );
236
237 // the schematic names a net that joins one pin, which the board leaves without a net
238 for( const auto& [name, pins] : schematicNets )
239 {
240 if( pins.size() != 1 || !name.StartsWith( wxS( "unconnected-(" ) ) || aBoard.FindNet( name ) )
241 continue;
242
243 auto bare = bareJoins.find( *pins.begin() );
244
245 if( bare == bareJoins.end() )
246 continue;
247
248 NETINFO_ITEM* net = new NETINFO_ITEM( &aBoard, name );
249 aBoard.Add( net );
250
251 for( PAD* pad : bare->second )
252 pad->SetNet( net );
253
254 result.m_unconnectedAssigned++;
255 }
256
257 aReporter.Report( wxString::Format( _( "Named %d board nets after the schematic, %d already matched, %d differ "
258 "and %d unconnected pads named.\n" ),
259 result.m_renamed, result.m_alreadyNamed, result.m_conflicting,
260 result.m_unconnectedAssigned ),
262
263 return result;
264}
265
266
268{
269 std::map<wxString, FOOTPRINT*> footprints = uniqueFootprints( aBoard );
270 int filled = 0;
271
272 // Update PCB from Schematic treats these as handled separately or not editable on the board
273 const std::set<wxString> skipped = { GetDefaultFieldName( FIELD_T::REFERENCE, UNTRANSLATED ),
276 wxS( "Component Class" ) };
277
278 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
279 {
280 COMPONENT* component = aNetlist.GetComponent( i );
281 auto found = footprints.find( component->GetReference() );
282
283 if( found == footprints.end() )
284 continue;
285
286 FOOTPRINT* footprint = found->second;
287 bool changed = false;
288
289 if( footprint->GetValue().IsEmpty() && !component->GetValue().IsEmpty() )
290 {
291 footprint->SetValue( component->GetValue() );
292 changed = true;
293 }
294
295 for( const auto& [name, value] : component->GetFields() )
296 {
297 if( skipped.count( name ) )
298 continue;
299
300 // an empty field still has to exist, as the schematic parity check looks for it
301 if( PCB_FIELD* existing = footprint->HasField( name ) ? footprint->GetField( name ) : nullptr )
302 {
303 if( existing->GetText().IsEmpty() && !value.IsEmpty() )
304 {
305 existing->SetText( value );
306 changed = true;
307 }
308
309 continue;
310 }
311
312 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER );
313 footprint->Add( field );
314
315 field->SetName( name );
316 field->SetText( value );
317 field->SetVisible( false );
318 field->SetLayer( footprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
319 field->SetPosition( footprint->GetPosition() );
320 field->Rotate( footprint->GetPosition(), footprint->GetOrientation() );
321 field->StyleFromSettings( aBoard.GetDesignSettings(), true );
322 changed = true;
323 }
324
325 if( changed )
326 filled++;
327 }
328
329 return filled;
330}
const char * name
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1520
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2797
const FOOTPRINTS & Footprints() const
Definition board.h:465
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
Used to store the component pin name to net name (and pin function) associations stored in a netlist.
Store all of the related component information found in a netlist.
const COMPONENT_NET & GetNet(unsigned aIndex) const
const wxString & GetReference() const
const wxString & GetValue() const
const nlohmann::ordered_map< wxString, wxString > & GetFields() const
unsigned GetNetCount() const
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
EDA_ANGLE GetOrientation() const
Definition footprint.h:382
bool HasField(const wxString &aFieldName) const
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:393
void SetValue(const wxString &aValue)
Definition footprint.h:822
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
const wxString & GetValue() const
Definition footprint.h:817
VECTOR2I GetPosition() const override
Definition footprint.h:379
Handle the data for a net.
Definition netinfo.h:50
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
unsigned GetCount() const
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
Definition pad.h:61
void SetName(const wxString &aName)
Definition pcb_field.h:121
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &aSettings, bool aCheckSide) override
Definition pcb_text.cpp:371
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:103
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition pcb_text.cpp:583
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
#define _(s)
bool ApplyImportedNetNameMap(BOARD &aBoard, const std::map< wxString, wxString > &aNames, REPORTER &aReporter)
Apply caller-selected imported net names without changing net identity or connectivity.
int ReconcileImportedReferences(BOARD &aBoard, NETLIST &aNetlist)
Give an imported board footprint the reference its schematic symbol uses.
int FillImportedFootprintFields(BOARD &aBoard, NETLIST &aNetlist)
Copy what an imported board footprint lacks from the schematic symbol it is linked to.
NET_RECONCILE_RESULT ReconcileImportedNetNames(BOARD &aBoard, NETLIST &aNetlist, REPORTER &aReporter)
Give each net of an imported board the name its schematic uses.
@ F_Fab
Definition layer_ids.h:115
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
@ RPT_SEVERITY_INFO
Outcome of naming the nets of an imported board after the nets of its schematic.
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.