Added NETINFO_MAPPING, to ease saving nets with consecutive net codes (without modifying the net codes during the run time).

Now, nets are saved with consecutive net codes (both modern & legacy plugins).
Zones are saved together with their nets, without depending on the fact if there are any pads with such net. Therefore validation of zone net names was removed (pcbnew/class_board.cpp).

Performed tests:
- Changed a pad's net name from empty to existent - ok, name was changed.
- Changed a pad's net name from empty to nonexistent - ok, error message is displayed, net name stays empty.
- Changed a pad's net name from existent to empty - ok, net name became empty
- Changed a pad's net name from existent to nonexistent - ok, error message is displayed, net name is not changed.

- Drawn a zone that belongs to a net, then modified schematics so the net does not exist anymore. After reloading the net list, all pads/tracks are updated. Zones still belongs to the net that does not exist in the schematic (but still exists in .kicad_pcb file). After running DRC, the zone becomes not filled.
- Undo & redo affects assignment of a polygon to a specific net (you may change net of a polygon, refill it and undo/redo the changes).

- KiCad s-expr & legacy, Eagle, P-CAD boards seem to load without any problem (they also contain correct net names assigned to the appropriate pads). All types of board file formats were loaded, then saved in sexpr format and reopened with a KiCad built from the master branch (without my modifications).
- A few boards were also saved using the legacy format and were opened with the master KiCad without any issues.

- Change a net name for a pad, restore with undo/redo - ok
- Remove everything, restore with undo - ok
- Remove everything, reload netlist - ok

Differences observed between files saved by the master branch KiCad and this one:
- list of nets are not saved in any particular order, so net codes may differ
- the default net class does not contain the unconnected net
This commit is contained in:
Maciej Suminski 2014-01-28 10:19:51 +01:00
parent 441e03172c
commit 15b8d3d20a
9 changed files with 240 additions and 72 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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<int/wxString, NETINFO_ITEM*>
class iterator
{
public:
iterator( std::map<int, int>::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<int, int>::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<int, int> 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<D_PAD*> m_PadsFullList; ///< contains all pads, sorted by pad's netname.
std::vector<D_PAD*> m_PadsFullList; ///< contains all pads, sorted by pad's netname.
///< can be used in ratsnest calculations.
};

View File

@ -11,6 +11,9 @@
#include <class_board.h>
#include <class_module.h>
#include <class_pad.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_netinfo.h>
@ -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<int, int>::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<int> 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<int>::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;

View File

@ -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;
}

View File

@ -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:
//-----</PLUGIN API>--------------------------------------------------------
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 );

View File

@ -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;
}

View File

@ -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 );

View File

@ -29,6 +29,7 @@
#include <errno.h>
#include <common.h>
#include <confirm.h>
#include <macros.h>
#include <convert_from_iu.h>
#include <trigo.h>
@ -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;