KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eeschema/cross-probing.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 (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <fmt.h>
27#include <kiface_base.h>
28#include <kiway.h>
29#include <kiway_express.h>
30#include <eda_dde.h>
31#include <connection_graph.h>
32#include <sch_sheet.h>
33#include <sch_symbol.h>
34#include <sch_reference_list.h>
35#include <string_utils.h>
39#include <project_sch.h>
40#include <richio.h>
41#include <tools/sch_actions.h>
43#include <advanced_config.h>
44
45#include <pgm_base.h>
48#include <widgets/kistatusbar.h>
49#include <wx/log.h>
50#include <trace_helpers.h>
51
52SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference,
53 bool aSearchHierarchy, SCH_SEARCH_T aSearchType,
54 const wxString& aSearchText )
55{
56 SCH_SHEET_PATH* sheetWithSymbolFound = nullptr;
57 SCH_SYMBOL* symbol = nullptr;
58 SCH_PIN* pin = nullptr;
59 SCH_SHEET_LIST sheetList;
60 SCH_ITEM* foundItem = nullptr;
61
62 if( !aSearchHierarchy )
63 sheetList.push_back( m_frame->GetCurrentSheet() );
64 else
65 sheetList = m_frame->Schematic().Hierarchy();
66
67 for( SCH_SHEET_PATH& sheet : sheetList )
68 {
69 SCH_SCREEN* screen = sheet.LastScreen();
70
71 for( EDA_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
72 {
73 SCH_SYMBOL* candidate = static_cast<SCH_SYMBOL*>( item );
74
75 // Search by path if specified, otherwise search by reference
76 bool found = false;
77
78 if( aPath )
79 {
80 wxString path = sheet.PathAsString() + candidate->m_Uuid.AsString();
81 found = ( *aPath == path );
82 }
83 else
84 {
85 found = ( aReference && aReference->CmpNoCase( candidate->GetRef( &sheet ) ) == 0 );
86 }
87
88 if( found )
89 {
90 symbol = candidate;
91 sheetWithSymbolFound = &sheet;
92
93 if( aSearchType == HIGHLIGHT_PIN )
94 {
95 pin = symbol->GetPin( aSearchText );
96
97 // Ensure we have found the right unit in case of multi-units symbol
98 if( pin )
99 {
100 int unit = pin->GetLibPin()->GetUnit();
101
102 if( unit != 0 && unit != symbol->GetUnit() )
103 {
104 pin = nullptr;
105 continue;
106 }
107
108 // Get pin position in true schematic coordinate
109 foundItem = pin;
110 break;
111 }
112 }
113 else
114 {
115 foundItem = symbol;
116 break;
117 }
118 }
119 }
120
121 if( foundItem )
122 break;
123 }
124
125 CROSS_PROBING_SETTINGS& crossProbingSettings = m_frame->eeconfig()->m_CrossProbing;
126
127 if( symbol )
128 {
129 if( *sheetWithSymbolFound != m_frame->GetCurrentSheet() )
130 {
131 m_frame->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet,
132 sheetWithSymbolFound );
133 }
134
135 if( crossProbingSettings.center_on_items )
136 {
137 if( crossProbingSettings.zoom_to_fit )
138 {
139 BOX2I bbox = symbol->GetBoundingBox();
140
141 m_toolMgr->GetTool<SCH_SELECTION_TOOL>()->ZoomFitCrossProbeBBox( bbox );
142 }
143
144 if( pin )
145 m_frame->FocusOnItem( pin );
146 else
147 m_frame->FocusOnItem( symbol );
148 }
149 }
150
151 /* Print diag */
152 wxString msg;
153 wxString displayRef;
154
155 if( aReference )
156 displayRef = *aReference;
157 else if( aPath )
158 displayRef = *aPath;
159
160 if( symbol )
161 {
162 if( aSearchType == HIGHLIGHT_PIN )
163 {
164 if( foundItem )
165 msg.Printf( _( "%s pin %s found" ), displayRef, aSearchText );
166 else
167 msg.Printf( _( "%s found but pin %s not found" ), displayRef, aSearchText );
168 }
169 else
170 {
171 msg.Printf( _( "%s found" ), displayRef );
172 }
173 }
174 else
175 {
176 msg.Printf( _( "%s not found" ), displayRef );
177 }
178
179 m_frame->SetStatusText( msg );
180 m_frame->GetCanvas()->Refresh();
181
182 return foundItem;
183}
184
185
186/* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER
187 *
188 * Commands are:
189 *
190 * $PART: "reference" Put cursor on symbol.
191 * $PART: "reference" $REF: "ref" Put cursor on symbol reference.
192 * $PART: "reference" $VAL: "value" Put cursor on symbol value.
193 * $PART: "reference" $PAD: "pin name" Put cursor on the symbol pin.
194 * $NET: "netname" Highlight a specified net
195 * $CLEAR: "HIGHLIGHTED" Clear symbols highlight
196 *
197 * $CONFIG Show the Manage Symbol Libraries dialog
198 * $ERC Show the ERC dialog
199 */
200void SCH_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
201{
203 char line[1024];
204
205 strncpy( line, cmdline, sizeof( line ) - 1 );
206 line[ sizeof( line ) - 1 ] = '\0';
207
208 char* idcmd = strtok( line, " \n\r" );
209 char* text = strtok( nullptr, "\"\n\r" );
210
211 if( idcmd == nullptr )
212 return;
213
214 CROSS_PROBING_SETTINGS& crossProbingSettings = eeconfig()->m_CrossProbing;
215
216 if( strcmp( idcmd, "$CONFIG" ) == 0 )
217 {
219 return;
220 }
221 else if( strcmp( idcmd, "$ERC" ) == 0 )
222 {
224 return;
225 }
226 else if( strcmp( idcmd, "$NET:" ) == 0 )
227 {
228 if( !crossProbingSettings.auto_highlight )
229 return;
230
231 wxString netName = From_UTF8( text );
232
233 if( auto sg = Schematic().ConnectionGraph()->FindFirstSubgraphByName( netName ) )
234 m_highlightedConn = sg->GetDriverConnection()->Name();
235 else
236 m_highlightedConn = wxEmptyString;
237
240
241 SetStatusText( _( "Highlighted net:" ) + wxS( " " ) + UnescapeString( netName ) );
242 return;
243 }
244 else if( strcmp( idcmd, "$CLEAR:" ) == 0 )
245 {
246 // Cross-probing is now done through selection so we no longer need a clear command
247 return;
248 }
249
250 if( !crossProbingSettings.on_selection )
251 return;
252
253 if( text == nullptr )
254 return;
255
256 if( strcmp( idcmd, "$PART:" ) != 0 )
257 return;
258
259 wxString part_ref = From_UTF8( text );
260
261 /* look for a complement */
262 idcmd = strtok( nullptr, " \n\r" );
263
264 if( idcmd == nullptr ) // Highlight symbol only (from CvPcb or Pcbnew)
265 {
266 // Highlight symbol part_ref, or clear Highlight, if part_ref is not existing
267 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
268 return;
269 }
270
271 text = strtok( nullptr, "\"\n\r" );
272
273 if( text == nullptr )
274 return;
275
276 wxString msg = From_UTF8( text );
277
278 if( strcmp( idcmd, "$REF:" ) == 0 )
279 {
280 // Highlighting the reference itself isn't actually that useful, and it's harder to
281 // see. Highlight the parent and display the message.
282 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
283 }
284 else if( strcmp( idcmd, "$VAL:" ) == 0 )
285 {
286 // Highlighting the value itself isn't actually that useful, and it's harder to see.
287 // Highlight the parent and display the message.
288 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, msg );
289 }
290 else if( strcmp( idcmd, "$PAD:" ) == 0 )
291 {
292 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_PIN, msg );
293 }
294 else
295 {
296 editor->FindSymbolAndItem( nullptr, &part_ref, true, HIGHLIGHT_SYMBOL, wxEmptyString );
297 }
298}
299
300
301void SCH_EDIT_FRAME::SendSelectItemsToPcb( const std::vector<EDA_ITEM*>& aItems, bool aForce )
302{
303 std::vector<wxString> parts;
304
305 for( EDA_ITEM* item : aItems )
306 {
307 switch( item->Type() )
308 {
309 case SCH_SYMBOL_T:
310 {
311 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
312 wxString ref = symbol->GetField( FIELD_T::REFERENCE )->GetText();
313
314 parts.push_back( wxT( "F" ) + EscapeString( ref, CTX_IPC ) );
315 break;
316 }
317
318 case SCH_SHEET_T:
319 {
320 // For cross probing, we need the full path of the sheet, because
321 // we search by the footprint path prefix in the PCB editor
322 wxString full_path = GetCurrentSheet().PathAsString() + item->m_Uuid.AsString();
323
324 parts.push_back( wxT( "S" ) + full_path );
325 break;
326 }
327
328 case SCH_PIN_T:
329 {
330 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
331 SYMBOL* symbol = pin->GetParentSymbol();
332 wxString ref = symbol->GetRef( &GetCurrentSheet(), false );
333
334 parts.push_back( wxT( "P" ) + EscapeString( ref, CTX_IPC ) + wxT( "/" )
335 + EscapeString( pin->GetShownNumber(), CTX_IPC ) );
336 break;
337 }
338
339 default:
340 break;
341 }
342 }
343
344 if( parts.empty() )
345 return;
346
347 std::string command = "$SELECT: 0,";
348
349 for( wxString part : parts )
350 {
351 command += part;
352 command += ",";
353 }
354
355 command.pop_back();
356
357 if( Kiface().IsSingle() )
358 {
359 SendCommand( MSG_TO_PCB, command );
360 }
361 else
362 {
363 // Typically ExpressMail is going to be s-expression packets, but since
364 // we have existing interpreter of the selection packet on the other
365 // side in place, we use that here.
367 command, this );
368 }
369}
370
371
372void SCH_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
373{
374 // The command is a keyword followed by a quoted string.
375
376 std::string packet = fmt::format( "$NET: \"{}\"", TO_UTF8( aNetName ) );
377
378 if( !packet.empty() )
379 {
380 if( Kiface().IsSingle() )
381 {
382 SendCommand( MSG_TO_PCB, packet );
383 }
384 else
385 {
386 // Typically ExpressMail is going to be s-expression packets, but since
387 // we have existing interpreter of the cross probe packet on the other
388 // side in place, we use that here.
390 }
391 }
392}
393
394
396{
397 if( !aConnection )
398 {
400 return;
401 }
402
403 if( aConnection->IsNet() )
404 {
405 SendCrossProbeNetName( aConnection->Name() );
406 return;
407 }
408
409 if( aConnection->Members().empty() )
410 return;
411
412 auto all_members = aConnection->AllMembers();
413
414 wxString nets = all_members[0]->Name();
415
416 if( all_members.size() == 1 )
417 {
418 SendCrossProbeNetName( nets );
419 return;
420 }
421
422 // TODO: This could be replaced by just sending the bus name once we have bus contents
423 // included as part of the netlist sent from Eeschema to Pcbnew (and thus Pcbnew can
424 // natively keep track of bus membership)
425
426 for( size_t i = 1; i < all_members.size(); i++ )
427 nets << "," << all_members[i]->Name();
428
429 std::string packet = fmt::format( "$NETS: \"{}\"", TO_UTF8( nets ) );
430
431 if( !packet.empty() )
432 {
433 if( Kiface().IsSingle() )
434 SendCommand( MSG_TO_PCB, packet );
435 else
436 {
437 // Typically ExpressMail is going to be s-expression packets, but since
438 // we have existing interpreter of the cross probe packet on the other
439 // side in place, we use that here.
441 }
442 }
443}
444
445
447{
448 std::string packet = "$CLEAR\n";
449
450 if( Kiface().IsSingle() )
451 {
452 SendCommand( MSG_TO_PCB, packet );
453 }
454 else
455 {
456 // Typically ExpressMail is going to be s-expression packets, but since
457 // we have existing interpreter of the cross probe packet on the other
458 // side in place, we use that here.
460 }
461}
462
463
465 const SCH_SHEET_LIST& aSchematicSheetList, const SCH_SHEET_PATH& aSheetPath,
466 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
467 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
468 bool aRecursive = false )
469{
470 if( aRecursive )
471 {
472 // Iterate over children
473 for( const SCH_SHEET_PATH& candidate : aSchematicSheetList )
474 {
475 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
476 continue;
477
478 findSymbolsAndPins( aSchematicSheetList, candidate, aSyncSymMap, aSyncPinMap,
479 aRecursive );
480 }
481 }
482
483 SCH_REFERENCE_LIST references;
484
485 aSheetPath.GetSymbols( references, false, true );
486
487 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
488 {
489 SCH_REFERENCE& schRef = references[ii];
490
491 if( schRef.IsSplitNeeded() )
492 schRef.Split();
493
494 SCH_SYMBOL* symbol = schRef.GetSymbol();
495 wxString refNum = schRef.GetRefNumber();
496 wxString fullRef = schRef.GetRef() + refNum;
497
498 // Skip power symbols
499 if( fullRef.StartsWith( wxS( "#" ) ) )
500 continue;
501
502 // Unannotated symbols are not supported
503 if( refNum.compare( wxS( "?" ) ) == 0 )
504 continue;
505
506 // Look for whole footprint
507 auto symMatchIt = aSyncSymMap.find( fullRef );
508
509 if( symMatchIt != aSyncSymMap.end() )
510 {
511 symMatchIt->second.emplace_back( schRef );
512
513 // Whole footprint was selected, no need to select pins
514 continue;
515 }
516
517 // Look for pins
518 auto symPinMatchIt = aSyncPinMap.find( fullRef );
519
520 if( symPinMatchIt != aSyncPinMap.end() )
521 {
522 std::unordered_map<wxString, SCH_PIN*>& pinMap = symPinMatchIt->second;
523 std::vector<SCH_PIN*> pinsOnSheet = symbol->GetPins( &aSheetPath );
524
525 for( SCH_PIN* pin : pinsOnSheet )
526 {
527 int pinUnit = pin->GetLibPin()->GetUnit();
528
529 if( pinUnit > 0 && pinUnit != schRef.GetUnit() )
530 continue;
531
532 auto pinIt = pinMap.find( pin->GetNumber() );
533
534 if( pinIt != pinMap.end() )
535 pinIt->second = pin;
536 }
537 }
538 }
539
540 return false;
541}
542
543
545 const SCH_SHEET_LIST& aSchematicSheetList, const SCH_SHEET_PATH& aSheetPath,
546 std::unordered_map<wxString, std::vector<SCH_REFERENCE>>& aSyncSymMap,
547 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>>& aSyncPinMap,
548 std::unordered_map<SCH_SHEET_PATH, bool>& aCache )
549{
550 auto cacheIt = aCache.find( aSheetPath );
551
552 if( cacheIt != aCache.end() )
553 return cacheIt->second;
554
555 // Iterate over children
556 for( const SCH_SHEET_PATH& candidate : aSchematicSheetList )
557 {
558 if( candidate == aSheetPath || !candidate.IsContainedWithin( aSheetPath ) )
559 continue;
560
561 bool childRet = sheetContainsOnlyWantedItems( aSchematicSheetList, candidate, aSyncSymMap,
562 aSyncPinMap, aCache );
563
564 if( !childRet )
565 {
566 aCache.emplace( aSheetPath, false );
567 return false;
568 }
569 }
570
571 SCH_REFERENCE_LIST references;
572 aSheetPath.GetSymbols( references, false, true );
573
574 if( references.GetCount() == 0 ) // Empty sheet, obviously do not contain wanted items
575 {
576 aCache.emplace( aSheetPath, false );
577 return false;
578 }
579
580 for( unsigned ii = 0; ii < references.GetCount(); ii++ )
581 {
582 SCH_REFERENCE& schRef = references[ii];
583
584 if( schRef.IsSplitNeeded() )
585 schRef.Split();
586
587 wxString refNum = schRef.GetRefNumber();
588 wxString fullRef = schRef.GetRef() + refNum;
589
590 // Skip power symbols
591 if( fullRef.StartsWith( wxS( "#" ) ) )
592 continue;
593
594 // Unannotated symbols are not supported
595 if( refNum.compare( wxS( "?" ) ) == 0 )
596 continue;
597
598 if( aSyncSymMap.find( fullRef ) == aSyncSymMap.end() )
599 {
600 aCache.emplace( aSheetPath, false );
601 return false; // Some symbol is not wanted.
602 }
603
604 if( aSyncPinMap.find( fullRef ) != aSyncPinMap.end() )
605 {
606 aCache.emplace( aSheetPath, false );
607 return false; // Looking for specific pins, so can't be mapped
608 }
609 }
610
611 aCache.emplace( aSheetPath, true );
612 return true;
613}
614
615
616std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>>
617findItemsFromSyncSelection( const SCHEMATIC& aSchematic, const std::string aSyncStr,
618 bool aFocusOnFirst )
619{
620 wxArrayString syncArray = wxStringTokenize( aSyncStr, wxS( "," ) );
621
622 std::unordered_map<wxString, std::vector<SCH_REFERENCE>> syncSymMap;
623 std::unordered_map<wxString, std::unordered_map<wxString, SCH_PIN*>> syncPinMap;
624 std::unordered_map<SCH_SHEET_PATH, double> symScores;
625 std::unordered_map<SCH_SHEET_PATH, bool> fullyWantedCache;
626
627 std::optional<wxString> focusSymbol;
628 std::optional<std::pair<wxString, wxString>> focusPin;
629 std::unordered_map<SCH_SHEET_PATH, std::vector<SCH_ITEM*>> focusItemResults;
630
631 const SCH_SHEET_LIST allSheetsList = aSchematic.Hierarchy();
632
633 // In orderedSheets, the current sheet comes first.
634 std::vector<SCH_SHEET_PATH> orderedSheets;
635 orderedSheets.reserve( allSheetsList.size() );
636 orderedSheets.push_back( aSchematic.CurrentSheet() );
637
638 for( const SCH_SHEET_PATH& sheetPath : allSheetsList )
639 {
640 if( sheetPath != aSchematic.CurrentSheet() )
641 orderedSheets.push_back( sheetPath );
642 }
643
644 // Init sync maps from the sync string
645 for( size_t i = 0; i < syncArray.size(); i++ )
646 {
647 wxString syncEntry = syncArray[i];
648
649 if( syncEntry.empty() )
650 continue;
651
652 wxString syncData = syncEntry.substr( 1 );
653
654 switch( syncEntry.GetChar( 0 ).GetValue() )
655 {
656 case 'F': // Select by footprint: F<Reference>
657 {
658 wxString symRef = UnescapeString( syncData );
659
660 if( aFocusOnFirst && ( i == 0 ) )
661 focusSymbol = symRef;
662
663 syncSymMap[symRef] = std::vector<SCH_REFERENCE>();
664 break;
665 }
666
667 case 'P': // Select by pad: P<Footprint reference>/<Pad number>
668 {
669 wxString symRef = UnescapeString( syncData.BeforeFirst( '/' ) );
670 wxString padNum = UnescapeString( syncData.AfterFirst( '/' ) );
671
672 if( aFocusOnFirst && ( i == 0 ) )
673 focusPin = std::make_pair( symRef, padNum );
674
675 syncPinMap[symRef][padNum] = nullptr;
676 break;
677 }
678
679 default:
680 break;
681 }
682 }
683
684 // Lambda definitions
685 auto flattenSyncMaps =
686 [&syncSymMap, &syncPinMap]() -> std::vector<SCH_ITEM*>
687 {
688 std::vector<SCH_ITEM*> allVec;
689
690 for( const auto& [symRef, symbols] : syncSymMap )
691 {
692 for( const SCH_REFERENCE& ref : symbols )
693 allVec.push_back( ref.GetSymbol() );
694 }
695
696 for( const auto& [symRef, pinMap] : syncPinMap )
697 {
698 for( const auto& [padNum, pin] : pinMap )
699 {
700 if( pin )
701 allVec.push_back( pin );
702 }
703 }
704
705 return allVec;
706 };
707
708 auto clearSyncMaps =
709 [&syncSymMap, &syncPinMap]()
710 {
711 for( auto& [symRef, symbols] : syncSymMap )
712 symbols.clear();
713
714 for( auto& [reference, pins] : syncPinMap )
715 {
716 for( auto& [number, pin] : pins )
717 pin = nullptr;
718 }
719 };
720
721 auto syncMapsValuesEmpty =
722 [&syncSymMap, &syncPinMap]() -> bool
723 {
724 for( const auto& [symRef, symbols] : syncSymMap )
725 {
726 if( symbols.size() > 0 )
727 return false;
728 }
729
730 for( const auto& [symRef, pins] : syncPinMap )
731 {
732 for( const auto& [padNum, pin] : pins )
733 {
734 if( pin )
735 return false;
736 }
737 }
738
739 return true;
740 };
741
742 auto checkFocusItems =
743 [&]( const SCH_SHEET_PATH& aSheet )
744 {
745 if( focusSymbol )
746 {
747 auto findIt = syncSymMap.find( *focusSymbol );
748
749 if( findIt != syncSymMap.end() )
750 {
751 if( findIt->second.size() > 0 )
752 focusItemResults[aSheet].push_back( findIt->second.front().GetSymbol() );
753 }
754 }
755 else if( focusPin )
756 {
757 auto findIt = syncPinMap.find( focusPin->first );
758
759 if( findIt != syncPinMap.end() )
760 {
761 if( findIt->second[focusPin->second] )
762 focusItemResults[aSheet].push_back( findIt->second[focusPin->second] );
763 }
764 }
765 };
766
767 auto makeRetForSheet =
768 [&]( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aFocusItem )
769 {
770 clearSyncMaps();
771
772 // Fill sync maps
773 findSymbolsAndPins( allSheetsList, aSheet, syncSymMap, syncPinMap );
774 std::vector<SCH_ITEM*> itemsVector = flattenSyncMaps();
775
776 // Add fully wanted sheets to vector
777 for( SCH_ITEM* item : aSheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
778 {
779 KIID_PATH kiidPath = aSheet.Path();
780 kiidPath.push_back( item->m_Uuid );
781
782 std::optional<SCH_SHEET_PATH> subsheetPath =
783 allSheetsList.GetSheetPathByKIIDPath( kiidPath );
784
785 if( !subsheetPath )
786 continue;
787
788 if( sheetContainsOnlyWantedItems( allSheetsList, *subsheetPath, syncSymMap,
789 syncPinMap, fullyWantedCache ) )
790 {
791 itemsVector.push_back( item );
792 }
793 }
794
795 return std::make_tuple( aSheet, aFocusItem, itemsVector );
796 };
797
798 if( aFocusOnFirst )
799 {
800 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
801 {
802 clearSyncMaps();
803
804 findSymbolsAndPins( allSheetsList, sheetPath, syncSymMap, syncPinMap );
805
806 checkFocusItems( sheetPath );
807 }
808
809 if( focusItemResults.size() > 0 )
810 {
811 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
812 {
813 const std::vector<SCH_ITEM*>& items = focusItemResults[sheetPath];
814
815 if( !items.empty() )
816 return makeRetForSheet( sheetPath, items.front() );
817 }
818 }
819 }
820 else
821 {
822 for( const SCH_SHEET_PATH& sheetPath : orderedSheets )
823 {
824 clearSyncMaps();
825
826 findSymbolsAndPins( allSheetsList, sheetPath, syncSymMap, syncPinMap );
827
828 if( !syncMapsValuesEmpty() )
829 {
830 // Something found on sheet
831 return makeRetForSheet( sheetPath, nullptr );
832 }
833 }
834 }
835
836 return std::nullopt;
837}
838
839
841{
842 std::string& payload = mail.GetPayload();
843
844 switch( mail.Command() )
845 {
847 {
848 std::stringstream ss( payload );
849 std::string file;
850
853 std::optional<LIBRARY_TABLE*> optTable = manager.Table( LIBRARY_TABLE_TYPE::SYMBOL,
855
856 wxCHECK_RET( optTable.has_value(), "Could not load symbol lib table." );
857 LIBRARY_TABLE* table = optTable.value();
858
859 while( std::getline( ss, file, '\n' ) )
860 {
861 if( file.empty() )
862 continue;
863
864 wxFileName fn( file );
866 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
867 bool success = true;
868
869 if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
870 {
871 wxLogTrace( "KIWAY", "Unknown file type: %s", fn.GetFullPath() );
872 continue;
873 }
874
875 pi.reset( SCH_IO_MGR::FindPlugin( type ) );
876
877 if( !table->HasRow( fn.GetName() ) )
878 {
879 LIBRARY_TABLE_ROW& row = table->InsertRow();
880 row.SetNickname( fn.GetName() );
881 row.SetURI( fn.GetFullPath() );
882 row.SetType( SCH_IO_MGR::ShowType( type ) );
883
884 table->Save().map_error(
885 [&]( const LIBRARY_ERROR& aError )
886 {
887 wxLogError( wxT( "Error saving project library table:\n\n" ) + aError.message );
888 success = false;
889 } );
890
891 if( success )
892 {
894 adapter->LoadOne( fn.GetName() );
895 }
896 }
897 }
898
902
903 break;
904 }
905
906 case MAIL_CROSS_PROBE:
907 ExecuteRemoteCommand( payload.c_str() );
908 break;
909
910 case MAIL_SELECTION:
911 if( !eeconfig()->m_CrossProbing.on_selection )
912 break;
913
915
917 {
918 // $SELECT: 0,<spec1>,<spec2>,<spec3>
919 // Try to select specified items.
920
921 // $SELECT: 1,<spec1>,<spec2>,<spec3>
922 // Select and focus on <spec1> item, select other specified items that are on the
923 // same sheet.
924
925 std::string prefix = "$SELECT: ";
926
927 std::string paramStr = payload.substr( prefix.size() );
928
929 // Empty/broken command: we need at least 2 chars for sync string.
930 if( paramStr.size() < 2 )
931 break;
932
933 std::string syncStr = paramStr.substr( 2 );
934
935 bool focusOnFirst = ( paramStr[0] == '1' );
936
937 std::optional<std::tuple<SCH_SHEET_PATH, SCH_ITEM*, std::vector<SCH_ITEM*>>> findRet =
938 findItemsFromSyncSelection( Schematic(), syncStr, focusOnFirst );
939
940 if( findRet )
941 {
942 auto& [sheetPath, focusItem, items] = *findRet;
943
944 m_syncingPcbToSchSelection = true; // recursion guard
945
946 GetToolManager()->GetTool<SCH_SELECTION_TOOL>()->SyncSelection( sheetPath, focusItem,
947 items );
948
950
951 if( eeconfig()->m_CrossProbing.flash_selection )
952 {
953 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE): flash enabled, items=%zu", items.size() );
954 if( items.empty() )
955 {
956 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE): nothing to flash" );
957 }
958 else
959 {
960 std::vector<SCH_ITEM*> itemPtrs;
961 std::copy( items.begin(), items.end(), std::back_inserter( itemPtrs ) );
962
963 StartCrossProbeFlash( itemPtrs );
964 }
965 }
966 else
967 {
968 wxLogTrace( traceCrossProbeFlash, "MAIL_SELECTION(_FORCE): flash disabled" );
969 }
970 }
971
972 break;
973 }
974
976 {
977 if( !payload.empty() )
978 {
979 wxString annotationMessage( payload );
980
981 // Ensure schematic is OK for netlist creation (especially that it is fully annotated):
982 if( !ReadyToNetlist( annotationMessage ) )
983 return;
984 }
985
986 if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
988
989 NETLIST_EXPORTER_KICAD exporter( &Schematic() );
990 STRING_FORMATTER formatter;
991
992 exporter.Format( &formatter, GNL_ALL | GNL_OPT_KICAD );
993
994 payload = formatter.GetString();
995 break;
996 }
997
999 {
1000 KIID uuid( payload );
1002
1003 if( SCH_ITEM* item = m_schematic->ResolveItem( uuid, &path, true ) )
1004 {
1005 if( item->Type() == SCH_SHEET_T )
1006 payload = static_cast<SCH_SHEET*>( item )->GetShownName( false );
1007 else if( item->Type() == SCH_SYMBOL_T )
1008 payload = static_cast<SCH_SYMBOL*>( item )->GetRef( &path, true );
1009 else
1010 payload = item->GetFriendlyName();
1011 }
1012
1013 break;
1014 }
1015
1017 try
1018 {
1019 SCH_EDITOR_CONTROL* controlTool = m_toolManager->GetTool<SCH_EDITOR_CONTROL>();
1020 controlTool->AssignFootprints( payload );
1021 }
1022 catch( const IO_ERROR& )
1023 {
1024 }
1025 break;
1026
1027 case MAIL_SCH_REFRESH:
1028 {
1030
1032 GetCanvas()->Refresh();
1033 break;
1034 }
1035
1036 case MAIL_IMPORT_FILE:
1037 {
1038 // Extract file format type and path (plugin type, path and properties keys, values
1039 // separated with \n)
1040 std::stringstream ss( payload );
1041 char delim = '\n';
1042
1043 std::string formatStr;
1044 wxCHECK( std::getline( ss, formatStr, delim ), /* void */ );
1045
1046 std::string fnameStr;
1047 wxCHECK( std::getline( ss, fnameStr, delim ), /* void */ );
1048
1049 int importFormat;
1050
1051 try
1052 {
1053 importFormat = std::stoi( formatStr );
1054 }
1055 catch( std::invalid_argument& )
1056 {
1057 wxFAIL;
1058 importFormat = -1;
1059 }
1060
1061 std::map<std::string, UTF8> props;
1062
1063 do
1064 {
1065 std::string key, value;
1066
1067 if( !std::getline( ss, key, delim ) )
1068 break;
1069
1070 std::getline( ss, value, delim ); // We may want an empty string as value
1071
1072 props.emplace( key, value );
1073
1074 } while( true );
1075
1076 if( importFormat >= 0 )
1077 importFile( fnameStr, importFormat, props.empty() ? nullptr : &props );
1078
1079 break;
1080 }
1081
1082 case MAIL_SCH_SAVE:
1083 if( SaveProject() )
1084 payload = "success";
1085
1086 break;
1087
1088 case MAIL_SCH_UPDATE:
1090 break;
1091
1092 case MAIL_RELOAD_LIB:
1093 {
1094 if( m_designBlocksPane && m_designBlocksPane->IsShown() )
1095 {
1096 m_designBlocksPane->RefreshLibs();
1097 SyncView();
1098 }
1099
1100 // Show any symbol library load errors in the status bar
1101 if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
1102 {
1104 wxString errors = adapter->GetLibraryLoadErrors();
1105
1106 if( !errors.IsEmpty() )
1107 statusBar->SetLoadWarningMessages( errors );
1108 }
1109
1110 break;
1111 }
1112
1113 default:;
1114
1115 }
1116}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
static TOOL_ACTION showSymbolLibTable
Definition actions.h:282
static TOOL_ACTION updateSchematicFromPcb
Definition actions.h:265
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CROSS_PROBING_SETTINGS m_CrossProbing
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:522
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition view.cpp:1561
Definition kiid.h:49
wxString AsString() const
Definition kiid.cpp:246
Carry a payload from one KIWAY_PLAYER to another within a PROJECT.
std::string & GetPayload()
Return the payload, which can be any text but it typically self identifying s-expression.
MAIL_T Command()
Returns the MAIL_T associated with this mail.
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:507
wxString GetLibraryLoadErrors() const
Returns all library load errors as newline-separated strings for display.
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
void LoadProjectTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the project library tables in the given list, or all tables if no list is given
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetURI(const wxString &aUri)
Generate the KiCad netlist format supported by Pcbnew.
void Format(OUTPUTFORMATTER *aOutputFormatter, int aCtl)
Output this s-expression netlist into aOutputFormatter.
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:134
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Holds all the data relating to one schematic.
Definition schematic.h:88
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:187
static TOOL_ACTION runERC
Inspection and Editing.
static TOOL_ACTION changeSheet
static TOOL_ACTION updateNetHighlighting
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SyncView()
Mark all items for refresh.
EESCHEMA_SETTINGS * eeconfig() const
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
bool IsNet() const
const std::vector< std::shared_ptr< SCH_CONNECTION > > AllMembers() const
wxString Name(bool aIgnoreSheet=false) const
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void AssignFootprints(const std::string &aChangedSetOfReferences)
SCH_ITEM * FindSymbolAndItem(const wxString *aPath, const wxString *aReference, bool aSearchHierarchy, SCH_SEARCH_T aSearchType, const wxString &aSearchText)
Find a symbol in the schematic and an item in this symbol and select it.
bool ReadyToNetlist(const wxString &aAnnotateMessage)
Check if we are ready to write a netlist file for the current schematic.
bool m_syncingPcbToSchSelection
friend class SCH_EDITOR_CONTROL
void SendSelectItemsToPcb(const std::vector< EDA_ITEM * > &aItems, bool aForce)
Send items to board editor for selection.
void SendCrossProbeClearHighlight()
Tell Pcbnew to clear the existing highlighted net, if one exists.
SCHEMATIC * m_schematic
The currently loaded schematic.
SCH_SHEET_PATH & GetCurrentSheet() const
void RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, PROGRESS_REPORTER *aProgressReporter=nullptr)
Generate the connection data for the entire schematic hierarchy.
SCHEMATIC & Schematic() const
void ExecuteRemoteCommand(const char *cmdline) override
Execute a remote command sent via a socket on port KICAD_SCH_PORT_SERVICE_NUMBER (which defaults to 4...
void RefreshNetNavigator(const NET_NAVIGATOR_ITEM_DATA *aSelection=nullptr)
SCH_DESIGN_BLOCK_PANE * m_designBlocksPane
bool importFile(const wxString &aFileName, int aFileType, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load the given filename but sets the path to the current project path.
void SendCrossProbeNetName(const wxString &aNetName)
Send a net name to Pcbnew for highlighting.
void StartCrossProbeFlash(const std::vector< SCH_ITEM * > &aItems)
wxString m_highlightedConn
The highlighted net or bus or empty string.
void SetCrossProbeConnection(const SCH_CONNECTION *aConnection)
Send a connection (net or bus) to Pcbnew for highlighting.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
bool SaveProject(bool aSaveAs=false)
Save the currently-open schematic (including its hierarchy) and associated project.
void KiwayMailIn(KIWAY_EXPRESS &aEvent) override
Receive KIWAY_EXPRESS messages from other players.
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:118
static const wxString ShowType(SCH_FILE_T aFileType)
Return a brief name for a plugin, given aFileType enum.
static SCH_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a symbol library using the file extension of aLibPath.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
int GetUnit() const
Definition sch_item.h:238
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
A helper to define a symbol's reference designator in a schematic.
void Split()
Attempt to split the reference designator into a name (U) and number (1).
bool IsSplitNeeded()
Determine if this reference needs to be split or if it likely already has been.
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
wxString GetRefNumber() const
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetSheetPathByKIIDPath(const KIID_PATH &aPath, bool aIncludeLastSheet=true) const
Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Adds SCH_REFERENCE object to aReferences for each symbol in the sheet.
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
bool IsContainedWithin(const SCH_SHEET_PATH &aSheetPathToTest) const
Check if this path is contained inside aSheetPathToTest.
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
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:76
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:422
const std::string & GetString()
Definition richio.h:445
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:63
virtual const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const =0
TOOL_MANAGER * m_toolManager
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
#define _(s)
bool SendCommand(int aService, const std::string &aMessage)
Used by a client to sent (by a socket connection) a data to a server.
Definition eda_dde.cpp:310
DDE server & client.
#define MSG_TO_PCB
Definition eda_dde.h:49
std::optional< std::tuple< SCH_SHEET_PATH, SCH_ITEM *, std::vector< SCH_ITEM * > > > findItemsFromSyncSelection(const SCHEMATIC &aSchematic, const std::string aSyncStr, bool aFocusOnFirst)
bool findSymbolsAndPins(const SCH_SHEET_LIST &aSchematicSheetList, const SCH_SHEET_PATH &aSheetPath, std::unordered_map< wxString, std::vector< SCH_REFERENCE > > &aSyncSymMap, std::unordered_map< wxString, std::unordered_map< wxString, SCH_PIN * > > &aSyncPinMap, bool aRecursive=false)
bool sheetContainsOnlyWantedItems(const SCH_SHEET_LIST &aSchematicSheetList, const SCH_SHEET_PATH &aSheetPath, std::unordered_map< wxString, std::vector< SCH_REFERENCE > > &aSyncSymMap, std::unordered_map< wxString, std::unordered_map< wxString, SCH_PIN * > > &aSyncPinMap, std::unordered_map< SCH_SHEET_PATH, bool > &aCache)
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:35
@ FRAME_SCH_VIEWER
Definition frame_type.h:36
@ FRAME_CVPCB
Definition frame_type.h:52
const wxChar *const traceCrossProbeFlash
Flag to enable debug output for cross-probe flash operations.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:637
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
@ MAIL_IMPORT_FILE
Definition mail_type.h:48
@ MAIL_SCH_REFRESH
Definition mail_type.h:53
@ MAIL_CROSS_PROBE
Definition mail_type.h:39
@ MAIL_SELECTION_FORCE
Definition mail_type.h:41
@ MAIL_SCH_SAVE
Definition mail_type.h:43
@ MAIL_ASSIGN_FOOTPRINTS
Definition mail_type.h:42
@ MAIL_SCH_GET_ITEM
Definition mail_type.h:50
@ MAIL_SCH_UPDATE
Definition mail_type.h:47
@ MAIL_ADD_LOCAL_LIB
Definition mail_type.h:54
@ MAIL_SCH_GET_NETLIST
Definition mail_type.h:49
@ MAIL_SELECTION
Definition mail_type.h:40
@ MAIL_RELOAD_LIB
Definition mail_type.h:57
@ ALL
All except INITIAL_ADD.
Definition view_item.h:59
#define GNL_ALL
@ GNL_OPT_KICAD
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
SCH_SEARCH_T
Schematic search type used by the socket link with Pcbnew.
@ HIGHLIGHT_SYMBOL
@ HIGHLIGHT_PIN
@ GLOBAL_CLEANUP
Definition schematic.h:77
KIWAY Kiway(KFCTL_STANDALONE)
wxString UnescapeString(const wxString &aSource)
wxString From_UTF8(const char *cstring)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
@ CTX_IPC
Cross-probing behavior.
bool on_selection
Synchronize the selection for multiple items too.
bool zoom_to_fit
Zoom to fit items (ignored if center_on_items is off).
bool center_on_items
Automatically pan to cross-probed items.
bool auto_highlight
Automatically turn on highlight mode in the target frame.
wxString message
@ REFERENCE
Field Reference of part, i.e. "IC21".
std::string path
KIBIS_PIN * pin
wxLogTrace helper definitions.
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_PIN_T
Definition typeinfo.h:157