/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2012 Wayne Stambaugh * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DIALOG_ERC::DIALOG_ERC( SCH_EDIT_FRAME* parent ) : DIALOG_ERC_BASE( parent, ID_DIALOG_ERC ), // parent looks for this ID explicitly m_initialized( false ) { m_parent = parent; m_lastMarkerFound = nullptr; wxFont infoFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); infoFont.SetSymbolicSize( wxFONTSIZE_SMALL ); m_textMarkers->SetFont( infoFont ); m_titleMessages->SetFont( infoFont ); Init(); // We use a sdbSizer to get platform-dependent ordering of the action buttons, but // that requires us to correct the button labels here. m_sdbSizer1OK->SetLabel( _( "Run" ) ); m_sdbSizer1Cancel->SetLabel( _( "Close" ) ); m_sdbSizer1->Layout(); m_sdbSizer1OK->SetDefault(); // Now all widgets have the size fixed, call FinishDialogSettings FinishDialogSettings(); } void DIALOG_ERC::Init() { m_initialized = false; SCH_SCREENS screens; updateMarkerCounts( &screens ); DisplayERC_MarkersList(); } void DIALOG_ERC::updateMarkerCounts( SCH_SCREENS *screens ) { int markers = screens->GetMarkerCount( MARKER_BASE::MARKER_ERC, MARKER_BASE::MARKER_SEVERITY_UNSPEC ); int warnings = screens->GetMarkerCount( MARKER_BASE::MARKER_ERC, MARKER_BASE::MARKER_SEVERITY_WARNING ); int errors = screens->GetMarkerCount( MARKER_BASE::MARKER_ERC, MARKER_BASE::MARKER_SEVERITY_ERROR ); wxString num; num.Printf( wxT( "%d" ), markers ); m_TotalErrCount->SetValue( num ); num.Printf( wxT( "%d" ), errors ); m_LastErrCount->SetValue( num ); num.Printf( wxT( "%d" ), warnings ); m_LastWarningCount->SetValue( num ); } /* Delete the old ERC markers, over the whole hierarchy */ void DIALOG_ERC::OnEraseDrcMarkersClick( wxCommandEvent& event ) { SCH_SCREENS ScreenList; ScreenList.DeleteAllMarkers( MARKER_BASE::MARKER_ERC ); updateMarkerCounts( &ScreenList ); m_MarkersList->ClearList(); m_parent->GetCanvas()->Refresh(); } // This is a modeless dialog so we have to handle these ourselves. void DIALOG_ERC::OnButtonCloseClick( wxCommandEvent& event ) { Close(); } void DIALOG_ERC::OnCloseErcDialog( wxCloseEvent& event ) { Destroy(); } void DIALOG_ERC::OnErcCmpClick( wxCommandEvent& event ) { wxBusyCursor busy; m_MarkersList->ClearList(); m_MessagesList->Clear(); wxSafeYield(); // m_MarkersList must be redraw WX_TEXT_CTRL_REPORTER reporter( m_MessagesList ); TestErc( reporter ); } void DIALOG_ERC::RedrawDrawPanel() { WINDOW_THAWER thawer( m_parent ); m_parent->GetCanvas()->Refresh(); } void DIALOG_ERC::OnLeftClickMarkersList( wxHtmlLinkEvent& event ) { wxString link = event.GetLinkInfo().GetHref(); m_lastMarkerFound = nullptr; long index; if( !link.ToLong( &index ) ) return; const SCH_MARKER* marker = m_MarkersList->GetItem( index ); if( !marker ) return; // Search for the selected marker unsigned i; SCH_SHEET_LIST sheetList( g_RootSheet ); bool found = false; for( i = 0; i < sheetList.size(); i++ ) { for( auto aItem : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) ) { if( static_cast( aItem ) == marker ) { found = true; break; } } if( found ) break; } if( !found ) // Error { wxMessageBox( _( "Marker not found" ) ); // The marker was deleted, so rebuild marker list DisplayERC_MarkersList(); return; } if( sheetList[i] != m_parent->GetCurrentSheet() ) { m_parent->GetToolManager()->RunAction( ACTIONS::cancelInteractive, true ); m_parent->GetToolManager()->RunAction( EE_ACTIONS::clearSelection, true ); m_parent->SetCurrentSheet( sheetList[i] ); m_parent->DisplayCurrentSheet(); sheetList[i].LastScreen()->SetZoom( m_parent->GetScreen()->GetZoom() ); m_parent->RedrawScreen( (wxPoint) m_parent->GetScreen()->m_ScrollCenter, false ); } m_lastMarkerFound = marker; m_parent->FocusOnLocation( marker->m_Pos ); RedrawDrawPanel(); } void DIALOG_ERC::OnLeftDblClickMarkersList( wxMouseEvent& event ) { // Remember: OnLeftClickMarkersList was called just before and therefore m_lastMarkerFound // was initialized (NULL if not found). if( m_lastMarkerFound ) { m_parent->FocusOnLocation( m_lastMarkerFound->m_Pos ); RedrawDrawPanel(); } Close(); } void DIALOG_ERC::DisplayERC_MarkersList() { SCH_SHEET_LIST sheetList( g_RootSheet); m_MarkersList->ClearList(); for( unsigned i = 0; i < sheetList.size(); i++ ) { for( auto aItem : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) ) { SCH_MARKER* marker = static_cast( aItem ); if( marker->GetMarkerType() == MARKER_BASE::MARKER_ERC ) m_MarkersList->AppendToList( marker ); } } m_MarkersList->DisplayList( GetUserUnits() ); } void DIALOG_ERC::TestErc( REPORTER& aReporter ) { wxFileName fn; // Build the whole sheet list in hierarchy (sheet, not screen) SCH_SHEET_LIST sheets( g_RootSheet ); sheets.AnnotatePowerSymbols(); if( m_parent->CheckAnnotate( aReporter, false ) ) { if( aReporter.HasMessage() ) aReporter.ReportTail( _( "Annotation required!" ), RPT_SEVERITY_ERROR ); return; } SCH_SCREENS screens; // Erase all previous DRC markers. screens.DeleteAllMarkers( MARKER_BASE::MARKER_ERC ); // Test duplicate sheet names inside a given sheet. While one can have multiple references // to the same file, each must have a unique name. TestDuplicateSheetNames( true ); TestConflictingBusAliases(); // The connection graph has a whole set of ERC checks it can run m_parent->RecalculateConnections( NO_CLEANUP ); g_ConnectionGraph->RunERC(); // Test is all units of each multiunit component have the same footprint assigned. TestMultiunitFootprints( sheets ); std::unique_ptr objectsConnectedList( m_parent->BuildNetListBase() ); // Reset the connection type indicator objectsConnectedList->ResetConnectionsType(); unsigned lastItemIdx = 0; unsigned nextItemIdx = 0; int MinConn = NOC; // Check that a pin appears in only one net. This check is necessary because multi-unit // components that have shared pins could be wired to different nets. std::unordered_map pin_to_net_map; // The netlist generated by SCH_EDIT_FRAME::BuildNetListBase is sorted by net number, which // means we can group netlist items into ranges that live in the same net. The range from // nextItem to the current item (exclusive) needs to be checked against the current item. // The lastItem variable is used as a helper to pass the last item's number from one loop // iteration to the next, which simplifies the initial pass. for( unsigned itemIdx = 0; itemIdx < objectsConnectedList->size(); itemIdx++ ) { auto item = objectsConnectedList->GetItem( itemIdx ); auto lastItem = objectsConnectedList->GetItem( lastItemIdx ); auto lastNet = lastItem->GetNet(); auto net = item->GetNet(); wxASSERT_MSG( lastNet <= net, wxT( "Netlist not correctly ordered" ) ); if( lastNet != net ) { // New net found: MinConn = NOC; nextItemIdx = itemIdx; } switch( item->m_Type ) { // These items do not create erc problems case NETLIST_ITEM::ITEM_UNSPECIFIED: case NETLIST_ITEM::SEGMENT: case NETLIST_ITEM::BUS: case NETLIST_ITEM::JUNCTION: case NETLIST_ITEM::LABEL: case NETLIST_ITEM::BUSLABELMEMBER: case NETLIST_ITEM::PINLABEL: case NETLIST_ITEM::GLOBBUSLABELMEMBER: break; // TODO(JE) Port this to the new system case NETLIST_ITEM::PIN: { // Check if this pin has appeared before on a different net if( item->m_Link ) { auto ref = item->GetComponentParent()->GetRef( &item->m_SheetPath ); wxString pin_name = ref + "_" + item->m_PinNum; if( pin_to_net_map.count( pin_name ) == 0 ) { pin_to_net_map[pin_name] = item->GetNetName(); } else if( pin_to_net_map[pin_name] != item->GetNetName() ) { SCH_MARKER* marker = new SCH_MARKER(); marker->SetData( ERCE_DIFFERENT_UNIT_NET, item->m_Start, wxString::Format( _( "Pin %s on %s is connected to both %s and %s" ), item->m_PinNum, ref, pin_to_net_map[pin_name], item->GetNetName() ), item->m_Start ); marker->SetMarkerType( MARKER_BASE::MARKER_ERC ); marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR ); item->m_SheetPath.LastScreen()->Append( marker ); } } // Look for ERC problems between pins: TestOthersItems( objectsConnectedList.get(), itemIdx, nextItemIdx, &MinConn ); break; } default: break; } lastItemIdx = itemIdx; } // Test similar labels (i;e. labels which are identical when // using case insensitive comparisons) if( m_parent->GetErcSettings().IsTestEnabled( ERCE_SIMILAR_GLBL_LABELS ) || m_parent->GetErcSettings().IsTestEnabled( ERCE_SIMILAR_LABELS ) ) { objectsConnectedList->TestforSimilarLabels(); } // Displays global results: updateMarkerCounts( &screens ); // Display diags: DisplayERC_MarkersList(); // Display new markers from the current screen: KIGFX::VIEW* view = m_parent->GetCanvas()->GetView(); for( auto item : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) ) view->Add( item ); m_parent->GetCanvas()->Refresh(); // Display message aReporter.ReportTail( _( "Finished" ), RPT_SEVERITY_INFO ); } wxDialog* InvokeDialogERC( SCH_EDIT_FRAME* aCaller ) { // This is a modeless dialog, so new it rather than instantiating on stack. DIALOG_ERC* dlg = new DIALOG_ERC( aCaller ); dlg->Show( true ); return dlg; // wxDialog is information hiding about DIALOG_ERC. }