diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 5c435f634a..b32e48ae22 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -2700,31 +2700,6 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, } } } - - // Verify zone net names validity: - // After schematic changes, a zone can have a non existing net name. - // It should be reported - if( aReporter && aReporter->ReportErrors() ) - { - //Loop through all copper zones - for( i = 0; i < m_ZoneDescriptorList.size(); i++ ) - { - ZONE_CONTAINER* zone = m_ZoneDescriptorList[i]; - - if( zone->GetNet() >= 0 || !zone->IsOnCopperLayer() ) - continue; - - // Net name not valid, report error - wxString coord; - coord << zone->GetPosition(); - msg.Printf( _( "** Error: Zone '%s' layer '%s'" - " has non-existent net name \"%s\" **\n" ), - GetChars( coord ), - GetChars( zone->GetLayerName() ), - GetChars( zone->GetNetname() ) ); - aReporter->Report( msg ); - } - } } /* Extracts the board outlines and build a closed polygon diff --git a/pcbnew/class_board_connected_item.cpp b/pcbnew/class_board_connected_item.cpp index 69bcfacb7e..26cca19772 100644 --- a/pcbnew/class_board_connected_item.cpp +++ b/pcbnew/class_board_connected_item.cpp @@ -63,9 +63,15 @@ void BOARD_CONNECTED_ITEM::SetNet( int aNetCode ) { m_netinfo = board->FindNet( aNetCode ); + // The requested net does not exist, mark it as unconnected if( m_netinfo == NULL ) m_netinfo = board->FindNet( NETINFO_LIST::UNCONNECTED ); } + else + { + // There is no board that contains list of nets, the item is orphaned + m_netinfo = &NETINFO_LIST::ORPHANED; + } } diff --git a/pcbnew/class_netinfo.h b/pcbnew/class_netinfo.h index 326a0fa3b4..50bb30457d 100644 --- a/pcbnew/class_netinfo.h +++ b/pcbnew/class_netinfo.h @@ -116,6 +116,121 @@ public: }; +class NETINFO_MAPPING +{ +public: + /** + * Function SetBoard + * Sets a BOARD object that is used to prepare the net code map. + */ + void SetBoard( const BOARD* aBoard ) + { + m_board = aBoard; + Update(); + } + + /** + * Function Update + * Prepares a mapping for net codes so they can be saved as consecutive numbers. + * To retrieve a mapped net code, use translateNet() function after calling this. + */ + void Update(); + + /** + * Function Translate + * Translates net number according to the map prepared by Update() function. It + * allows to have items stored with consecutive net codes. + * @param aNetCode is an old net code. + * @return Net code that follows the mapping. + */ + int Translate( int aNetCode ) const; + + ///> Wrapper class, so you can iterate through NETINFO_ITEM*s, not + ///> std::pair + class iterator + { + public: + iterator( std::map::const_iterator aIter, const NETINFO_MAPPING* aMapping ) : + m_iterator( aIter ), m_mapping( aMapping ) + { + } + + /// pre-increment operator + const iterator& operator++() + { + ++m_iterator; + + return *this; + } + + /// post-increment operator + iterator operator++( int ) + { + iterator ret = *this; + ++m_iterator; + + return ret; + } + + NETINFO_ITEM* operator*() const; + + NETINFO_ITEM* operator->() const; + + bool operator!=( const iterator& aOther ) const + { + return m_iterator != aOther.m_iterator; + } + + bool operator==( const iterator& aOther ) const + { + return m_iterator == aOther.m_iterator; + } + + private: + std::map::const_iterator m_iterator; + const NETINFO_MAPPING* m_mapping; + }; + + /** + * Function begin() + * Returns iterator to the first entry in the mapping. + * NOTE: The entry is a pointer to the original NETINFO_ITEM object, this it contains + * not mapped net code. + */ + iterator begin() const + { + return iterator( m_netMapping.begin(), this ); + } + + /** + * Function end() + * Returns iterator to the last entry in the mapping. + * NOTE: The entry is a pointer to the original NETINFO_ITEM object, this it contains + * not mapped net code. + */ + iterator end() const + { + return iterator( m_netMapping.end(), this ); + } + + /** + * Function GetSize + * @return Number of mapped nets (i.e. not empty nets for a given BOARD object). + */ + int GetSize() const + { + return m_netMapping.size(); + } + +private: + ///> Board for which mapping is prepared + const BOARD* m_board; + + ///> Map that allows saving net codes with consecutive numbers (for compatibility reasons) + std::map m_netMapping; +}; + + /** * Class NETINFO * is a container class for NETINFO_ITEM elements, which are the nets. That makes @@ -304,16 +419,12 @@ private: */ int getFreeNetCode() const; - BOARD* m_Parent; + BOARD* m_Parent; NETNAMES_MAP m_netNames; ///< map for a fast look up by net names NETCODES_MAP m_netCodes; ///< map for a fast look up by net codes - static int m_newNetCode; ///< number that has a *high* chance to be unused - ///< (to be sure, it is advised to use - ///< getFreeNetCode() function) - - std::vector m_PadsFullList; ///< contains all pads, sorted by pad's netname. + std::vector m_PadsFullList; ///< contains all pads, sorted by pad's netname. ///< can be used in ratsnest calculations. }; diff --git a/pcbnew/class_netinfolist.cpp b/pcbnew/class_netinfolist.cpp index e57bbfed8b..34b2eded9a 100644 --- a/pcbnew/class_netinfolist.cpp +++ b/pcbnew/class_netinfolist.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include +#include #include @@ -98,7 +101,7 @@ void NETINFO_LIST::buildListOfNets() { pad = m_PadsFullList[ii]; - if( pad->GetNet() == 0 ) // pad not connected + if( pad->GetNet() == NETINFO_LIST::UNCONNECTED ) // pad not connected continue; // Add pad to the appropriate list of pads @@ -172,15 +175,79 @@ void NETINFO_LIST::buildPadsFullList() int NETINFO_LIST::getFreeNetCode() const { + static int m_newNetCode = 0; + do { if( m_newNetCode < 0 ) m_newNetCode = 0; - } while( m_netCodes.count( ++NETINFO_LIST::m_newNetCode ) != 0 ); + } while( m_netCodes.count( ++m_newNetCode ) != 0 ); return m_newNetCode; } +int NETINFO_MAPPING::Translate( int aNetCode ) const +{ + std::map::const_iterator value = m_netMapping.find( aNetCode ); + + if( value != m_netMapping.end() ) + return value->second; + + // There was no entry for the given net code + return aNetCode; +} + + +void NETINFO_MAPPING::Update() +{ + // Collect all the used nets + std::set nets; + + // Be sure that the unconnected gets 0 and is mapped as 0 + nets.insert( 0 ); + + // Zones + for( int i = 0; i < m_board->GetAreaCount(); ++i ) + nets.insert( m_board->GetArea( i )->GetNet() ); + + // Tracks + for( TRACK* track = m_board->m_Track; track; track = track->Next() ) + nets.insert( track->GetNet() ); + + // Modules/pads + for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) + { + for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() ) + { + nets.insert( pad->GetNet() ); + } + } + + // Segzones + for( SEGZONE* zone = m_board->m_Zone; zone; zone = zone->Next() ) + nets.insert( zone->GetNet() ); + + // Prepare the new mapping + m_netMapping.clear(); + + // Now the nets variable stores all the used net codes (not only for pads) + int newNetCode = 0; + for( std::set::const_iterator it = nets.begin(), itEnd = nets.end(); it != itEnd; ++it ) + m_netMapping[*it] = newNetCode++; +} + + +NETINFO_ITEM* NETINFO_MAPPING::iterator::operator*() const +{ + return m_mapping->m_board->FindNet( m_iterator->first ); +} + + +NETINFO_ITEM* NETINFO_MAPPING::iterator::operator->() const +{ + return m_mapping->m_board->FindNet( m_iterator->first ); +} + + const NETINFO_ITEM NETINFO_LIST::ORPHANED = NETINFO_ITEM( NULL, wxString::FromUTF8( "orphaned" ), -1 ); const int NETINFO_LIST::UNCONNECTED = 0; -int NETINFO_LIST::m_newNetCode = 0; diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 3cda985754..fbdcf0fd3e 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -380,6 +380,9 @@ void PCB_IO::Save( const wxString& aFileName, BOARD* aBoard, const PROPERTIES* a m_board = aBoard; // after init() + // Prepare net mapping that assures that net codes saved in a file are consecutive integers + m_mapping->SetBoard( aBoard ); + FILE_OUTPUTFORMATTER formatter( aFileName ); m_out = &formatter; // no ownership @@ -499,7 +502,7 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const m_out->Print( aNestLevel+1, "(tracks %d)\n", aBoard->GetNumSegmTrack() ); m_out->Print( aNestLevel+1, "(zones %d)\n", aBoard->GetNumSegmZone() ); m_out->Print( aNestLevel+1, "(modules %d)\n", aBoard->m_Modules.GetCount() ); - m_out->Print( aNestLevel+1, "(nets %d)\n", aBoard->GetNetCount() ); + m_out->Print( aNestLevel+1, "(nets %d)\n", (int) m_mapping->GetSize() ); m_out->Print( aNestLevel, ")\n\n" ); aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl ); @@ -654,20 +657,14 @@ void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const m_out->Print( aNestLevel, ")\n\n" ); - // Unconditionally save the unconnected net - m_out->Print( aNestLevel, "(net 0 \"\")\n" ); - - // and now the rest of nets - for( NETINFO_LIST::iterator net( aBoard->BeginNet() ), netEnd( aBoard->EndNet() ); + // Save net codes and names + for( NETINFO_MAPPING::iterator net = m_mapping->begin(), netEnd = m_mapping->end(); net != netEnd; ++net ) { - if( net->GetNodesCount() > 0 ) // save only not empty nets - { - m_out->Print( aNestLevel, "(net %d %s)\n", - net->GetNet(), - m_out->Quotew( net->GetNetname() ).c_str() ); - } - } + m_out->Print( aNestLevel, "(net %d %s)\n", + m_mapping->Translate( net->GetNet() ), + m_out->Quotew( net->GetNetname() ).c_str() ); + } m_out->Print( 0, "\n" ); @@ -1234,7 +1231,8 @@ void PCB_IO::format( D_PAD* aPad, int aNestLevel ) const // Unconnected pad is default net so don't save it. if( !(m_ctl & CTL_OMIT_NETS) && aPad->GetNet() != 0 ) - StrPrintf( &output, " (net %d %s)", aPad->GetNet(), m_out->Quotew( aPad->GetNetname() ).c_str() ); + StrPrintf( &output, " (net %d %s)", m_mapping->Translate( aPad->GetNet() ), + m_out->Quotew( aPad->GetNetname() ).c_str() ); if( aPad->GetPadToDieLength() != 0 ) StrPrintf( &output, " (die_length %s)", FMT_IU( aPad->GetPadToDieLength() ).c_str() ); @@ -1391,7 +1389,7 @@ void PCB_IO::format( TRACK* aTrack, int aNestLevel ) const m_out->Print( 0, " (layer %s)", m_out->Quotew( aTrack->GetLayerName() ).c_str() ); } - m_out->Print( 0, " (net %d)", aTrack->GetNet() ); + m_out->Print( 0, " (net %d)", m_mapping->Translate( aTrack->GetNet() ) ); if( aTrack->GetTimeStamp() != 0 ) m_out->Print( 0, " (tstamp %lX)", aTrack->GetTimeStamp() ); @@ -1410,7 +1408,7 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const // so be sure a dummy value is stored, just for ZONE_CONTAINER compatibility // (perhaps netcode and netname should be not stored) m_out->Print( aNestLevel, "(zone (net %d) (net_name %s)", - aZone->GetIsKeepout() ? 0 : aZone->GetNet(), + aZone->GetIsKeepout() ? 0 : m_mapping->Translate( aZone->GetNet() ), m_out->Quotew( aZone->GetIsKeepout() ? wxT("") : aZone->GetNetname() ).c_str() ); formatLayer( aZone ); @@ -1627,20 +1625,11 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const } -PCB_IO::PCB_IO() : - m_cache( 0 ), - m_ctl( CTL_FOR_BOARD ), // expecting to OUTPUTFORMAT into BOARD files. - m_parser( new PCB_PARSER() ) -{ - init( 0 ); - m_out = &m_sf; -} - - PCB_IO::PCB_IO( int aControlFlags ) : m_cache( 0 ), m_ctl( aControlFlags ), - m_parser( new PCB_PARSER() ) + m_parser( new PCB_PARSER() ), + m_mapping( new NETINFO_MAPPING() ) { init( 0 ); m_out = &m_sf; @@ -1651,6 +1640,7 @@ PCB_IO::~PCB_IO() { delete m_cache; delete m_parser; + delete m_mapping; } diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index ce7c40ef38..95986e7531 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -32,6 +32,7 @@ class BOARD; class BOARD_ITEM; class FP_CACHE; class PCB_PARSER; +class NETINFO_MAPPING; /// Current s-expression file format version. 2 was the last legacy format version. @@ -122,9 +123,7 @@ public: //------------------------------------------------------------- - PCB_IO(); - - PCB_IO( int aControlFlags ); + PCB_IO( int aControlFlags = CTL_FOR_BOARD ); ~PCB_IO(); @@ -171,6 +170,8 @@ protected: OUTPUTFORMATTER* m_out; ///< output any Format()s to this, no ownership int m_ctl; PCB_PARSER* m_parser; + NETINFO_MAPPING* m_mapping; ///< mapping for net codes, so only not empty net codes + ///< are stored with consecutive integers as net codes /// we only cache one footprint library, this determines which one. void cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName = wxEmptyString ); diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 8b09403302..6e08682234 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -2899,6 +2899,8 @@ do { \ void LEGACY_PLUGIN::SaveBOARD( const BOARD* aBoard ) const { + m_mapping->SetBoard( aBoard ); + saveGENERAL( aBoard ); saveSHEET( aBoard ); @@ -2947,7 +2949,7 @@ void LEGACY_PLUGIN::saveGENERAL( const BOARD* aBoard ) const fprintf( m_fp, "Nzone %d\n", aBoard->GetNumSegmZone() ); fprintf( m_fp, "BoardThickness %s\n", fmtBIU( aBoard->GetDesignSettings().GetBoardThickness() ).c_str() ); fprintf( m_fp, "Nmodule %d\n", aBoard->m_Modules.GetCount() ); - fprintf( m_fp, "Nnets %d\n", aBoard->GetNetCount() ); + fprintf( m_fp, "Nnets %d\n", m_mapping->GetSize() ); fprintf( m_fp, "$EndGENERAL\n\n" ); } @@ -3088,9 +3090,11 @@ void LEGACY_PLUGIN::saveSETUP( const BOARD* aBoard ) const void LEGACY_PLUGIN::saveBOARD_ITEMS( const BOARD* aBoard ) const { // save the nets - int netcount = aBoard->GetNetCount(); - for( int i = 0; i < netcount; ++i ) - saveNETINFO_ITEM( aBoard->FindNet( i ) ); + for( NETINFO_MAPPING::iterator net = m_mapping->begin(), netEnd = m_mapping->end(); + net != netEnd; ++net ) + { + saveNETINFO_ITEM( *net ); + } // Saved nets do not include netclass names, so save netclasses after nets. saveNETCLASSES( &aBoard->m_NetClasses ); @@ -3148,7 +3152,8 @@ void LEGACY_PLUGIN::saveBOARD_ITEMS( const BOARD* aBoard ) const void LEGACY_PLUGIN::saveNETINFO_ITEM( const NETINFO_ITEM* aNet ) const { fprintf( m_fp, "$EQUIPOT\n" ); - fprintf( m_fp, "Na %d %s\n", aNet->GetNet(), EscapedUTF8( aNet->GetNetname() ).c_str() ); + fprintf( m_fp, "Na %d %s\n", m_mapping->Translate( aNet->GetNet() ), + EscapedUTF8( aNet->GetNetname() ).c_str() ); fprintf( m_fp, "St %s\n", "~" ); fprintf( m_fp, "$EndEQUIPOT\n" ); @@ -4417,7 +4422,8 @@ LEGACY_PLUGIN::LEGACY_PLUGIN() : m_props( 0 ), m_reader( 0 ), m_fp( 0 ), - m_cache( 0 ) + m_cache( 0 ), + m_mapping( new NETINFO_MAPPING() ) { init( NULL ); } @@ -4426,4 +4432,5 @@ LEGACY_PLUGIN::LEGACY_PLUGIN() : LEGACY_PLUGIN::~LEGACY_PLUGIN() { delete m_cache; + delete m_mapping; } diff --git a/pcbnew/legacy_plugin.h b/pcbnew/legacy_plugin.h index c5eba40083..dec3bfcbac 100644 --- a/pcbnew/legacy_plugin.h +++ b/pcbnew/legacy_plugin.h @@ -44,6 +44,7 @@ class NETCLASSES; class ZONE_CONTAINER; class DIMENSION; class NETINFO_ITEM; +class NETINFO_MAPPING; class TEXTE_MODULE; class EDGE_MODULE; class TRACK; @@ -124,6 +125,9 @@ protected: int m_loading_format_version; ///< which BOARD_FORMAT_VERSION am I Load()ing? LP_CACHE* m_cache; + NETINFO_MAPPING* m_mapping; ///< mapping for net codes, so only not empty net codes + ///< are stored with consecutive integers as net codes + /// initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed. void init( const PROPERTIES* aProperties ); diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index db9ed25acf..d7456b06ea 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -2417,13 +2418,19 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER() throw( IO_ERROR, PARSE_ERROR ) // Init the net code only, not the netname, to be sure // the zone net name is the name read in file. // (When mismatch, the user will be prompted in DRC, to fix the actual name) - zone->BOARD_CONNECTED_ITEM::SetNet( parseInt( "net number" ) ); + zone->SetNet( parseInt( "net number" ) ); NeedRIGHT(); break; case T_net_name: NeedSYMBOLorNUMBER(); - assert( m_board->FindNet( zone->GetNet() )->GetNetname() == FromUTF8() ); + if( m_board->FindNet( zone->GetNet() )->GetNetname() != FromUTF8() ) + { + DisplayError( NULL, wxString::Format( _( "There is a zone that belongs to a not " + "existing net (%s), you should verify it." ), + FromUTF8() ) ); + zone->SetNet( NETINFO_LIST::UNCONNECTED ); + } NeedRIGHT(); break;