KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_dump.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "conn_dump.h"
21#include "conn_facade.h"
22
23#include <json_common.h>
24#include <netclass.h>
25#include <project.h>
28#include <sch_bus_entry.h>
29#include <sch_connection.h>
30#include <sch_line.h>
31#include <sch_pin.h>
32#include <sch_screen.h>
33#include <sch_sheet.h>
34#include <sch_sheet_pin.h>
35#include <sch_symbol.h>
36#include <schematic.h>
37
38#include <map>
39#include <set>
40#include <stdexcept>
41
42
43namespace
44{
45using JSON = nlohmann::json;
46using KEY = std::pair<std::string, std::string>;
47using ROWS = std::map<KEY, JSON>;
48
49std::string utf8( const wxString& aText )
50{
51 return aText.ToStdString( wxConvUTF8 );
52}
53
54
55std::string serialize( ROWS& aRows )
56{
57 JSON result = JSON::array();
58
59 for( auto& [key, row] : aRows )
60 result.push_back( std::move( row ) );
61
62 return result.dump( 2 ) + "\n";
63}
64
65
66const char* typeName( CONNECTION_TYPE aType )
67{
68 switch( aType )
69 {
70 case CONNECTION_TYPE::NET: return "net";
71 case CONNECTION_TYPE::BUS: return "vector";
72 case CONNECTION_TYPE::BUS_GROUP: return "group";
73 case CONNECTION_TYPE::NONE: return "none";
74 }
75
76 return "none";
77}
78
79
80JSON resolvedClasses( const wxString& aName, NET_SETTINGS* aSettings )
81{
82 std::set<std::string> classes;
83
84 if( aSettings )
85 {
86 for( const NETCLASS* constituent : aSettings->GetEffectiveNetClass( aName )->GetConstituentNetclasses() )
87 classes.insert( utf8( constituent->GetName() ) );
88 }
89
90 return classes;
91}
92
93
94JSON valueRow( const wxString& aName, const wxString& aLocalName, const char* aType,
95 SCH_ITEM* aDriver, const KIID_PATH& aDriverPath, NET_SETTINGS* aSettings )
96{
97 return { { "name", utf8( aName ) }, { "local_name", utf8( aLocalName ) }, { "type", aType },
98 { "driver", aDriver ? utf8( aDriver->m_Uuid.AsString() ) : "" },
99 { "driver_path", aDriver ? utf8( aDriverPath.AsString() ) : "" },
100 { "members", JSON::array() }, { "netclasses", resolvedClasses( aName, aSettings ) } };
101}
102
103
104JSON memberRow( const SCH_CONNECTIVITY::BUS_SCHEMA::NODE& aNode,
105 const SCH_CONNECTIVITY::BUS_MEMBERS& aMembers, const wxString& aPath,
106 const wxString& aNamePrefix, const wxString& aLocalPrefix, SCH_ITEM* aDriver,
107 const KIID_PATH& aDriverPath, NET_SETTINGS* aSettings )
108{
110
111 if( aNode.kind == NODE::KIND::NET )
112 {
113 const auto& leaf = aMembers.leaves.at( aNode.leaf.value() );
114 return valueRow( leaf.Name(), aMembers.schema->leaves.at( aNode.leaf.value() ).name,
115 "net", leaf.Driver(), leaf.Sheet(), aSettings );
116 }
117
118 const wxString localName = aLocalPrefix + aNode.text;
119 JSON row = valueRow( aPath + aNamePrefix + aNode.text, localName,
120 aNode.kind == NODE::KIND::VECTOR ? "vector" : "group", aDriver, aDriverPath, aSettings );
121 wxString namePrefix = aNamePrefix;
122 wxString localPrefix = aLocalPrefix;
123
124 if( aNode.kind == NODE::KIND::GROUP && !aNode.prefix.empty() )
125 {
126 namePrefix += aNode.prefix + ".";
127 localPrefix += aNode.prefix + ".";
128 }
129
130 for( const auto& member : aNode.members )
131 {
132 row["members"].push_back( memberRow( member, aMembers, aPath, namePrefix, localPrefix, aDriver, aDriverPath,
133 aSettings ) );
134 }
135
136 return row;
137}
138
139
140JSON connectionRow( const SCH_CONNECTIVITY::ITEM_VIEW& aView,
142 CONNECTION_TYPE aType, NET_SETTINGS* aSettings )
143{
144 const wxString name = aView.Name();
145 SCH_ITEM* driver = aView.Driver();
146 const KIID_PATH driverPath = aView.Sheet();
147 JSON row = valueRow( name, aView.FullLocalName(), typeName( aType ), driver, driverPath, aSettings );
148 const auto members = aView.Members();
149
150 if( !members.tree )
151 return row;
152
153 const auto groupPrefix = [&]( const wxString& aPrefix )
154 {
155 if( aPrefix.empty() )
156 return wxString();
157
158 return aComponent.suffix ? wxString::Format( "%s_%u.", aPrefix, aComponent.suffix ) : aPrefix + wxS( "." );
159 };
160 const bool group = members.tree->kind == SCH_CONNECTIVITY::BUS_SCHEMA::NODE::KIND::GROUP;
161 const wxString localPrefix = group ? groupPrefix( members.tree->prefix ) : wxString();
162 const auto& best = aComponent.content->best;
163
164 // Members are named after the canonical group even where this row spells another prefix
165 const wxString namePrefix = group && best && best->schema
166 && best->schema->shape == SCH_CONNECTIVITY::BUS_SCHEMA::SHAPE::GROUP
167 ? groupPrefix( best->schema->prefix )
168 : localPrefix;
169 const wxString path = name.Left( name.length() - aView.Name( true ).length() );
170
171 for( const auto& member : members.tree->members )
172 {
173 row["members"].push_back( memberRow( member, members, path, namePrefix, localPrefix, driver, driverPath,
174 aSettings ) );
175 }
176
177 return row;
178}
179
180
181JSON connectionRow( const SCH_CONNECTION* aConnection, NET_SETTINGS* aSettings )
182{
183 if( !aConnection )
184 return valueRow( wxString(), wxString(), "none", nullptr, KIID_PATH(), nullptr );
185
186 const wxString name = !aConnection->Driver() && aConnection->Name( true ) == "<NO NET>"
187 ? wxString() : aConnection->Name();
188 JSON row = valueRow( name, aConnection->FullLocalName(), typeName( aConnection->Type() ), aConnection->Driver(),
189 aConnection->Sheet().Path(), aSettings );
190
191 // Member order encodes positional bus alignment, so only item rows are sorted
192 for( const auto& member : aConnection->Members() )
193 row["members"].push_back( connectionRow( member.get(), aSettings ) );
194
195 return row;
196}
197}
198
199
200std::string SCH_CONNECTIVITY::Dump( SCHEMATIC& aSchematic )
201{
202 ROWS rows;
203 NET_SETTINGS* settings = aSchematic.IsValid()
204 ? aSchematic.Project().GetProjectFile().NetSettings().get()
205 : nullptr;
206
207 for( const SCH_SHEET_PATH& path : aSchematic.Hierarchy() )
208 {
209 const std::string pathId = utf8( path.Path().AsString() );
210 const auto addItem =
211 [&]( SCH_ITEM* aItem )
212 {
213 const std::string itemId = utf8( aItem->m_Uuid.AsString() );
214 JSON row = connectionRow( aItem->Connection( &path ), settings );
215 row["path"] = pathId;
216 row["item"] = itemId;
217 row["dangling"] = aItem->IsDangling();
218 row["dangling_ends"] = { aItem->IsDangling() };
219
220 if( auto* line = dynamic_cast<SCH_LINE*>( aItem ) )
221 {
222 row["dangling_ends"] = { line->IsStartDangling(), line->IsEndDangling() };
223 }
224 else if( auto* entry = dynamic_cast<SCH_BUS_ENTRY_BASE*>( aItem ) )
225 {
226 row["dangling_ends"] = { entry->IsStartDangling(), entry->IsEndDangling() };
227 }
228
229 std::set<std::string> connected;
230
231 for( const SCH_ITEM* neighbour : aItem->ConnectedItems( path ) )
232 connected.insert( utf8( neighbour->m_Uuid.AsString() ) );
233
234 row["connected"] = connected;
235
236 if( !rows.emplace( KEY{ pathId, itemId }, std::move( row ) ).second )
237 throw std::runtime_error( "Duplicate connectivity item " + pathId + itemId );
238 };
239
240 for( SCH_ITEM* item : path.LastScreen()->Items() )
241 {
242 if( item->Type() == SCH_SYMBOL_T )
243 {
244 for( SCH_PIN* pin : static_cast<SCH_SYMBOL*>( item )->GetPins( &path ) )
245 addItem( pin );
246 }
247 else if( item->Type() == SCH_SHEET_T )
248 {
249 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
250 addItem( pin );
251 }
252 else if( item->IsConnectable() )
253 {
254 addItem( item );
255 }
256 }
257 }
258
259 return serialize( rows );
260}
261
262
263std::string SCH_CONNECTIVITY::Dump( SCHEMATIC& aSchematic, const FACADE& aFacade )
264{
265 const auto& publication = aFacade.Published();
266 const auto& keys = aFacade.Keys();
267 const auto& auxiliary = publication.Auxiliary();
268 std::unique_ptr<NET_SETTINGS> settings;
269
270 if( aSchematic.IsValid() )
271 {
272 auto source = aSchematic.Project().GetProjectFile().NetSettings();
273 settings = std::make_unique<NET_SETTINGS>( nullptr, "" );
274 settings->CopyFrom( *source );
275 aFacade.ApplyNetclasses( *settings );
276
277 // Chain patterns are shared consumer context, not the legacy graph's label assignments
278 for( const auto& [owner, patterns] : source->GetChainPatternAssignments() )
279 {
280 for( const auto& [matcher, name] : patterns )
281 settings->SetChainPatternAssignment( owner, matcher->GetPattern(), name );
282 }
283 }
284
285 std::map<INST_ID, SCH_SCREEN*> screens;
286
287 for( const SCH_SHEET_PATH& path : aSchematic.BuildSheetListSortedByPageNumbers() )
288 {
289 if( auto instance = keys.FindInstance( path.Path() ) )
290 screens.emplace( *instance, path.LastScreen() );
291 }
292
293 ROWS rows;
294
295 for( const auto& [key, result] : publication.Rows() )
296 {
297 const KIID_PATH& path = keys.Instance( key.inst );
298 auto view = aFacade.Connection( key.item, path );
299
300 if( !view )
301 throw std::runtime_error( "Cannot dump stale connectivity publication" );
302
303 JSON row = connectionRow( *view, publication.Components().at( result.component ), result.itemType,
304 settings.get() );
305 const std::string pathId = utf8( path.AsString() );
306 const std::string itemId = utf8( key.item.AsString() );
307 row["path"] = pathId;
308 row["item"] = itemId;
309 const auto& island = auxiliary.Islands().at( auxiliary.IslandOf().at( key ) ).value;
310 const auto dangling = island.dangling.at( key.item );
311 row["dangling"] = dangling != 0;
312 row["dangling_ends"] = { dangling != 0 };
313 const auto screen = screens.find( key.inst );
314
315 if( screen == screens.end() || !screen->second )
316 throw std::runtime_error( "Cannot dump connectivity without its source instance" );
317
318 SCH_ITEM* item = screen->second->GetConnectivityItem( key.item );
319
320 if( !item )
321 throw std::runtime_error( "Cannot dump connectivity without its source item" );
322
323 if( dynamic_cast<SCH_LINE*>( item ) || dynamic_cast<SCH_BUS_ENTRY_BASE*>( item ) )
324 row["dangling_ends"] = { ( dangling & 1 ) != 0, ( dangling & 2 ) != 0 };
325
326 // Undriven items keep the type their connection gets when it is created
328 && ( item->Type() == SCH_PIN_T || item->Type() == SCH_BUS_WIRE_ENTRY_T
329 || ( item->Type() == SCH_LINE_T && item->GetLayer() != LAYER_BUS ) ) )
330 {
331 row["type"] = typeName( CONNECTION_TYPE::NET );
332 }
333
334 std::set<std::string> connected;
335 const auto neighbors = auxiliary.NeighborsOf().find( key );
336
337 if( neighbors != auxiliary.NeighborsOf().end() )
338 {
339 for( const KIID& neighbor : neighbors->second )
340 connected.insert( utf8( neighbor.AsString() ) );
341 }
342
343 row["connected"] = connected;
344 rows.emplace( KEY{ pathId, itemId }, std::move( row ) );
345 }
346
347 return serialize( rows );
348}
const char * name
const KIID m_Uuid
Definition eda_item.h:597
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
wxString AsString() const
Definition kiid.cpp:423
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
const std::vector< NETCLASS * > & GetConstituentNetclasses() const
Gets the netclasses which make up this netclass.
Definition netclass.cpp:312
NET_SETTINGS stores various net-related settings in a project context.
std::shared_ptr< NETCLASS > GetEffectiveNetClass(const wxString &aNetName)
Fetches the effective (may be aggregate) netclass for the given net name.
std::shared_ptr< NET_SETTINGS > & NetSettings()
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
Holds all the data relating to one schematic.
Definition schematic.h:149
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:171
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:289
Base class for a bus or wire entry.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString FullLocalName() const
SCH_SHEET_PATH Sheet() const
CONNECTION_TYPE Type() const
wxString Name(bool aIgnoreSheet=false) const
SCH_ITEM * Driver() const
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
Model-owned source resolver.
void ApplyNetclasses(NET_SETTINGS &aSettings) const
Replace label-derived assignments from a complete publication; invalidate changed net caches.
const SESSION_KEYS & Keys() const
std::optional< ITEM_VIEW > Connection(const KIID &aItem, const KIID_PATH &aPath) const
const PUBLICATION & Published() const
BUS_MEMBERS Members() const
wxString Name(bool aIgnoreSheet=false) const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
const std::vector< SCH_ITEM * > & ConnectedItems(const SCH_SHEET_PATH &aPath) const
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:765
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:352
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
Schematic symbol object.
Definition sch_symbol.h:74
@ LAYER_BUS
Definition layer_ids.h:475
std::string Dump(SCHEMATIC &aSchematic)
Canonical, pointer-free connectivity rows for migration and rebuild comparisons.
CONNECTION_TYPE
@ BUS
This item represents a bus vector.
@ NET
This item represents a net.
@ NONE
No connection to this item.
@ BUS_GROUP
This item represents a bus group.
Query-time snapshot; reacquire after Update.
std::shared_ptr< const BUS_SCHEMA > schema
std::vector< NET_VIEW > leaves
One node of the presentation tree.
Definition conn_bus.h:72
wxString text
Container label text.
Definition conn_bus.h:82
std::optional< size_t > leaf
Leaf ordinal of a NET node.
Definition conn_bus.h:86
std::vector< NODE > members
Definition conn_bus.h:84
@ GROUP
A list such as I2C{SDA SCL}. Members align by name with another group.
Definition conn_bus.h:52
One published net or bus with its identity.
uint32_t suffix
Zero for the holder of the base name.
std::shared_ptr< const COMPONENT_CONTENT > content
std::string path
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:157
@ SCH_PIN_T
Definition typeinfo.h:149