Commit a2b3a4cf authored by Dick Hollenbeck's avatar Dick Hollenbeck
Browse files

more work on generic netlist export

parent 81bcc8d7
Loading
Loading
Loading
Loading
+7 −6
Original line number Diff line number Diff line
@@ -143,11 +143,12 @@ public:
     */
    wxString         Path();

    /** Function PathHumanReadable
     * Return the sheet path in a readable form, i.e.
     * as a path made from sheet names.
     * (the "normal" path uses the time stamps which do not changes even when
     * editing sheet parameters)
    /**
     * Function PathHumanReadable
     * returns the sheet path in a human readable form, i.e. as a path made
     * from sheet names.  The the "normal" path instead uses the time
     * stamps in the path.  (Time stamps do not change even when editing
     * sheet parameters).
     */
    wxString         PathHumanReadable();

+8 −8
Original line number Diff line number Diff line
@@ -32,25 +32,25 @@ enum LibrEntryOptions


/**
 * Base class to describe library components and aliases.
 * Class CMP_LIB_ENTRY
 * is a base class to describe library components and aliases.
 *
 * This class is not to be used directly.
 */
class CMP_LIB_ENTRY : public EDA_BaseStruct
{

protected:
    wxString         name;

    /** Library object that entry is attached to. */
    /// Library object that entry is attached to.
    CMP_LIBRARY*     library;

    /** Entry type, either ROOT or ALIAS. */
    /// Entry type, either ROOT or ALIAS.
    LibrEntryType    type;

    wxString         description;  /* documentation for info */
    wxString         keyWords;     /* keyword list (used for search for components by keyword) */
    wxString         docFileName;  /* Associate doc file name */
    wxString         description;  ///< documentation for info
    wxString         keyWords;     ///< keyword list (used for search for components by keyword)
    wxString         docFileName;  ///< Associate doc file name

public:
    CMP_LIB_ENTRY( LibrEntryType aType, const wxString& aName, CMP_LIBRARY* aLibrary = NULL );
+57 −34
Original line number Diff line number Diff line
@@ -51,27 +51,25 @@ extern bool operator<( const CMP_LIBRARY& item1, const CMP_LIBRARY& item2 );


/**
 * Component library object.
 *
 * Component libraries are used to load, save, search, and otherwise manipulate
 * Class CMP_LIBRARY
 * is used to load, save, search, and otherwise manipulate
 * component library files.
 */

class CMP_LIBRARY
{
public:
    int            m_Type;         /* type indicator */
    int            m_Type;          ///< type indicator
    int            m_Flags;

protected:
    wxFileName     fileName;        /* Library file name. */
    wxDateTime     timeStamp;       /* Library save time and date. */
    int            versionMajor;    /* Library major version number. */
    int            versionMinor;    /* Library minor version number. */
    LIB_ENTRY_LIST entries;         /* Parts themselves are saved here. */
    bool           isCache;         /* False for the "standard" libraries,
                                     * True for the library cache */
    wxString       header;          /* first line of loaded library. */
    wxFileName     fileName;        ///< Library file name.
    wxDateTime     timeStamp;       ///< Library save time and date.
    int            versionMajor;    ///< Library major version number.
    int            versionMinor;    ///< Library minor version number.
    LIB_ENTRY_LIST entries;         ///< Parts themselves are saved here.
    bool           isCache;         /**< False for the "standard" libraries,
                                         True for the library cache */
    wxString       header;          ///< first line of loaded library.

    static CMP_LIBRARY_LIST libraryList;
    static wxArrayString    libraryListSortOrder;
@@ -79,7 +77,7 @@ protected:
    friend class CMP_LIB_ENTRY;

private:
    bool           isModified;      /* Library modification status. */
    bool           isModified;      ///< Library modification status.

public:
    CMP_LIBRARY( int aType, const wxFileName& aFileName );
@@ -96,8 +94,9 @@ public:
    bool getModifyFlag( ) { return isModified;}

    /**
     * Save library to file.
     *
     * Function Save
     * saves library to a file.
     * <p>
     * Prior to component library version 3.0, two files were created.  The
     * component objects are wer as component library (*.lib) files.  The
     * library entry ojbect document strings were save in library document
@@ -142,8 +141,9 @@ private:
    bool LoadHeader( FILE* aFile, int* aLineNum );
    void LoadAliases( LIB_COMPONENT* aComponent );

    /** function RemoveEntryName
     * Remove an /a aName entry from the library list names.
    /**
     * Function RemoveEntryName
     * removes an /a aName entry from the library list names.
     * Warning: this is a partiel remove, because if aname is an alias
     * it is not removed from its root component.
     * this is for internal use only
@@ -164,7 +164,8 @@ public:
    }

    /**
     * Get the number of entries in the library.
     * Function GetCount
     * returns the number of entries in the library.
     *
     * @return The number of component and alias entries.
     */
@@ -350,14 +351,25 @@ public:
    wxString GetName() const { return fileName.GetName(); }

    /**
     * Return the full file library name with path and extension.
     * Function GetFullFileName
     * returns the full file library name with path and extension.
     *
     * @return Full library file name with path and extension.
     * @return wxString - Full library file name with path and extension.
     */
    wxString GetFullFileName() { return fileName.GetFullPath(); }

    /**
     * Set the component library file name.
     * Function GetLogicalName
     * returns the logical name of the library, which for now is the filename
     * without path or extension.
     * wxString - The logical library name.
     */
    wxString GetLogicalName() { return fileName.GetName(); }


    /**
     * Function SetFileName
     * sets the component library file name.
     *
     * @param aFileName - New library file name.
     */
@@ -375,15 +387,18 @@ public:
     */

    /**
     * Test for an existing library.
     * Function LibraryExists
     * tests for existence of a library.
     *
     * @param aLibptr - aLibptr.
     * @return true found.  false if not found.
     * @return bool - true if exists, else false
     */

    static bool LibraryExists( const CMP_LIBRARY* aLibptr );

    /**
     * Load a component library file.
     * Function LoadLibrary
     * loads a component library file.
     *
     * @param aFileName - File name of the component library to load.
     * @param aErrorMsg - Error message if the component library failed to load.
@@ -394,7 +409,8 @@ public:
                                     wxString& aErrorMsg );

    /**
     * Add a compnent library to the library list.
     * Function AddLibrary
     * adds a compnent library to the library list.
     *
     * @param aFileName - File name object of component library.
     * @param aErrorMsg - Error message if the component library failed to load.
@@ -403,7 +419,8 @@ public:
    static bool AddLibrary( const wxFileName& aFileName, wxString& aErrorMsg );

    /**
     * Insert a compnent library to the library list.
     * Function AddLibrary
     * inserts a compnent library into the library list.
     *
     * @param aFileName - File name object of component library.
     * @param aErrerMsg - Error message if the component library failed to load.
@@ -414,14 +431,16 @@ public:
                            CMP_LIBRARY_LIST::iterator& aIterator );

    /**
     * Remove component library from the library list.
     * Function RemoveLibrary
     * removes a component library from the library list.
     *
     * @param aName - Name of component library to remove.
     */
    static void RemoveLibrary( const wxString& aName );

    /**
     * Find component library by /a aName.
     * Function FindLibrary
     * finds a component library by \a aName.
     *
     * @param aName - Library file name without path or extension to find.
     * @return Component library if found, otherwise NULL.
@@ -429,7 +448,8 @@ public:
    static CMP_LIBRARY* FindLibrary( const wxString& aName );

    /**
     * Get the list of component library file names without path and extension.
     * Function GetLibraryNames
     * returns the list of component library file names without path and extension.
     *
     * @param aSorted - Sort the list of name if true.  Otherwise use the
     *                  library load order.
@@ -438,7 +458,8 @@ public:
    static wxArrayString GetLibraryNames( bool aSorted = true );

    /**
     * Search all libraries in the list for a component.
     * Function FindLibraryComponent
     * searches all libraries in the list for a component.
     *
     * A component object will always be returned.  If the entry found
     * is an alias.  The root component will be found and returned.
@@ -451,7 +472,8 @@ public:
                                                const wxString& aLibraryName = wxEmptyString );

    /**
     * Search all libraries in the list for an entry.
     * Function FindLibraryEntry
     * searches all libraries in the list for an entry.
     *
     * The object can be either a component or an alias.
     *
@@ -463,7 +485,8 @@ public:
                                            const wxString& aLibraryName = wxEmptyString );

    /**
     * Remove all cache libraries from library list.
     * Function RemoveCacheLibrary
     * removes all cache libraries from library list.
     */
    static void RemoveCacheLibrary();

+223 −65
Original line number Diff line number Diff line
/*
 * This program source code file is part of KICAD, a free EDA CAD application.
 *
 * Copyright (C) 1992-2009 jean-pierre.charras@gipsa-lab.inpg.fr
 * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 1992-2010 Kicad Developers, see change_log.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
 */

/*****************************/
/* Net list generation code. */
/*****************************/


#include "fctsys.h"

#include <wx/xml/xml.h>
@@ -39,13 +65,14 @@ static void WriteListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f,
                                bool use_netnames );

static bool WriteGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjectsList );
static bool WriteGENERICListOfNetsTxt( FILE* f, NETLIST_OBJECT_LIST& aObjectsList );
static bool WriteGENERICListOfNets( wxXmlNode* aNode, NETLIST_OBJECT_LIST& aObjectsList );

static void AddPinToComponentPinList( SCH_COMPONENT*  Component,
                                      SCH_SHEET_PATH* sheet,
                                      LIB_PIN*        PinEntry );

static void FindAllsInstancesOfComponent( SCH_COMPONENT*  Component,
static void FindAllInstancesOfComponent(  SCH_COMPONENT*  Component,
                                          LIB_COMPONENT*  aEntry,
                                          SCH_SHEET_PATH* Sheet_in );

@@ -220,14 +247,18 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( EDA_BaseStruct* item,
        if( !entry )
            continue;

        // Multi parts per package: test if already visited:
        // If Multi parts per package
        if( entry->GetPartCount() > 1 )
        {
            // test if already visited, and if so skip
            if( s_ReferencesAlreadyFound.Lookup( ref ) )
                continue;

            // Collect all parts and pins for this first occurance of reference
            FindAllInstancesOfComponent( comp, entry, path );
        }

        if( entry->GetPartCount() <= 1 )   // One part per package
        else    // entry->GetPartCount() <= 1 means one part per package
        {
            LIB_PIN_LIST pins;

@@ -242,9 +273,6 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( EDA_BaseStruct* item,
                AddPinToComponentPinList( comp, path, pin );
            }
        }
        else    // Multiple parts per package: Collect all parts and pins for
                // this reference
            FindAllsInstancesOfComponent( comp, entry, path );

        // Sort pins in s_SortedComponentPinList by pin number
        sort( s_SortedComponentPinList.begin(),
@@ -302,19 +330,28 @@ static wxString ReturnPinNetName( NETLIST_OBJECT* Pin, const wxString& DefaultFo
}


static wxXmlNode* Node( const wxString& name, const wxString& content=wxEmptyString )
/**
 * Function Node
 * is a convenience function that creates a new wxXmlNode with an optional textual child.
 * @param aName is the name to associate with a new node of type wxXML_ELEMENT_NODE.
 * @param aContent is optional, and if given is the text to include in a child
 *   of the returned node, and has type wxXML_TEXT_NODE.
 */
static wxXmlNode* Node( const wxString& aName, const wxString& aTextualContent = wxEmptyString )
{
    wxXmlNode* n = new wxXmlNode( 0, wxXML_ELEMENT_NODE, name );
    wxXmlNode* n = new wxXmlNode( 0, wxXML_ELEMENT_NODE, aName );

    if( content.Len() > 0 )     // excludes wxEmptyString
        n->AddChild( new wxXmlNode( 0, wxXML_TEXT_NODE, wxEmptyString, content ) );
    if( aTextualContent.Len() > 0 )     // excludes wxEmptyString, the default textual content
        n->AddChild( new wxXmlNode( 0, wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );

    return n;
}


/* Create a generic netlist, and call an external netlister
 *  to change the netlist syntax and create the file
/**
 * Function Write_GENERIC_NetList
 * creates a generic netlist, now in XML.
 * @return bool - true if there were no errors, else false.
 */
bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFileName )
{
@@ -325,11 +362,15 @@ bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFi
    wxXmlNode*      xroot;      // root node
    wxXmlNode*      xcomps;     // start of components

    wxString        timeStamp;

    // some strings we need many times, but don't want to construct more
    // than once for performance.  These are used within loops so the
    // enclosing wxString constructor would fire on each usage.
    // enclosing wxString constructor would fire on each loop iteration if
    // they were in a nested scope.

    wxString        timeStamp;
    wxString        logicalLibName;

    // these are actually constructor invocations, not assignments as it appears:
    const wxString  sFields     = wxT( "fields" );
    const wxString  sField      = wxT( "field" );
    const wxString  sComponent  = wxT( "comp" );          // use "part" ?
@@ -338,10 +379,17 @@ bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFi
    const wxString  sPins       = wxT( "pins" );
    const wxString  sPin        = wxT( "pin" );
    const wxString  sValue      = wxT( "value" );
    const wxString  sSheetPath  = wxT( "sheetpath" );
    const wxString  sFootprint  = wxT( "footprint" );
    const wxString  sDatasheet  = wxT( "datasheet" );
    const wxString  sTStamp     = wxT( "tstamp" );
    const wxString  sTStamps    = wxT( "tstamps" );
    const wxString  sTSFmt      = wxT( "%8.8lX" );        // comp->m_TimeStamp
    const wxString  sLibSource  = wxT( "libsource" );
    const wxString  sLibPart    = wxT( "libpart" );
    const wxString  sLib        = wxT( "lib" );
    const wxString  sPart       = wxT( "part" );
    const wxString  sNames      = wxT( "names" );

    s_ReferencesAlreadyFound.Clear();

@@ -364,16 +412,17 @@ bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFi

            schItem = comp;

            // current component being constructed
            wxXmlNode* xcomp = Node( sComponent );
            xcomps->AddChild( xcomp );
            xcomp->AddProperty( sRef, comp->GetRef( path ) );
            wxXmlNode* xcomp;  // current component being constructed

            xcomp->AddChild( Node( sValue, comp->GetField( VALUE )->m_Text ) );
            // Output the component's elments in order of expected access frequency.
            // This may not always look best, but it will allow faster execution
            // under XSL processing systems which do sequential searching within
            // an element.

            timeStamp.Printf( sTSFmt, comp->m_TimeStamp );
            xcomps->AddChild( xcomp = Node( sComponent ) );
            xcomp->AddProperty( sRef, comp->GetRef( path ) );

            xcomp->AddChild( Node( sTStamp, timeStamp ) );
            xcomp->AddChild( Node( sValue, comp->GetField( VALUE )->m_Text ) );

            if( !comp->GetField( FOOTPRINT )->m_Text.IsEmpty() )
                xcomp->AddChild( Node( sFootprint, comp->GetField( FOOTPRINT )->m_Text ) );
@@ -381,37 +430,60 @@ bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFi
            if( !comp->GetField( DATASHEET )->m_Text.IsEmpty() )
                xcomp->AddChild( Node( sDatasheet, comp->GetField( DATASHEET )->m_Text ) );

            // all fields within the component, starting with the MANDATORY_FIELDS
            // Export all user defined fields within the component,
            // which start at field index MANDATORY_FIELDS.  Only output the <fields>
            // container element if there are any <field>s.
            if( comp->GetFieldCount() > MANDATORY_FIELDS )
            {
                wxXmlNode* xfields = Node( sFields );
                xcomp->AddChild( xfields );
                wxXmlNode* xfields;
                xcomp->AddChild( xfields = Node( sFields ) );

                for( int fldNdx = MANDATORY_FIELDS; fldNdx < comp->GetFieldCount(); ++fldNdx )
                {
                    SCH_FIELD*  f = comp->GetField( fldNdx );
                    wxXmlNode*  xfield = Node( sField, f->m_Text );

                    // only output a field if non empty
                    if( !f->m_Text.IsEmpty() )
                    {
                        wxXmlNode*  xfield;
                        xfields->AddChild( xfield = Node( sField, f->m_Text ) );
                        xfield->AddProperty( sName, f->m_Name );
                    xfields->AddChild( xfield );
                    }
                }
            }

            wxXmlNode*  xlibsource;
            xcomp->AddChild( xlibsource = Node( sLibSource ) );

            // "logical" library name, which is in anticipation of a better search
            // algorithm for parts based on "logical_lib.part" and where logical_lib
            // is merely the library name minus path and extension.
            LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->m_ChipName );
            if( entry )
                xlibsource->AddProperty( sLib, entry->GetLibrary()->GetLogicalName()  );
            xlibsource->AddProperty( sPart, comp->m_ChipName );

            // @todo add:  libsource + sheetpath
            wxXmlNode* xsheetpath;
            xcomp->AddChild( xsheetpath = Node( sSheetPath ) );
            xsheetpath->AddProperty( sTStamps, path->Path() );
            xsheetpath->AddProperty( sNames, path->PathHumanReadable() );

            timeStamp.Printf( sTSFmt, comp->m_TimeStamp );
            xcomp->AddChild( Node( sTStamp, timeStamp ) );
        }
    }

    // @todo generate the nested <libpart> s
    xroot->AddChild( Node( wxT( "libparts" ) ) );

    // @todo generate the nested <net>s
    xroot->AddChild( Node( wxT("nets") ) );
    wxXmlNode* xnets;
    xroot->AddChild( xnets = Node( wxT( "nets" ) ) );
    WriteGENERICListOfNets( xnets, g_NetObjectslist );

    return xdoc.Save( aOutFileName, 2 /* indent bug, today was ignored by wxXml lib */ );

#else
#else   // ouput the well established/old net list format which was not XML.

    // ouput the well established/old net list format
    wxString        field;
    wxString        footprint;
    wxString        netname;
@@ -491,7 +563,7 @@ bool Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& aOutFi

    ret |= fprintf( out, "\n$BeginNets\n" );

    if( !WriteGENERICListOfNets( out, g_NetObjectslist ) )
    if( !WriteGENERICListOfNetsTxt( out, g_NetObjectslist ) )
        ret = -1;

    ret |= fprintf( out, "$EndNets\n" );
@@ -669,7 +741,7 @@ static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, bool use_
 */
static bool WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with_pcbnew )
{
    wxString    Line;
    wxString    field;
    wxString    footprint;
    char        dateBuf[256];
    int         ret = 0;        // zero now, OR in the sign bit on error
@@ -723,23 +795,23 @@ static bool WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with
            else
                footprint = wxT( "$noname" );

            Line = comp->GetRef( path );
            field = comp->GetRef( path );

            ret |= fprintf( f, " ( %s %s",
                    CONV_TO_UTF8( comp->GetPath( path ) ),
                    CONV_TO_UTF8( footprint ) );

            ret |= fprintf( f, "  %s", CONV_TO_UTF8( Line ) );
            ret |= fprintf( f, "  %s", CONV_TO_UTF8( field ) );

            Line = comp->GetField( VALUE )->m_Text;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fprintf( f, " %s", CONV_TO_UTF8( Line ) );
            field = comp->GetField( VALUE )->m_Text;
            field.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fprintf( f, " %s", CONV_TO_UTF8( field ) );

            if( with_pcbnew )  // Add the lib name for this component
            {
                Line = comp->m_ChipName;
                Line.Replace( wxT( " " ), wxT( "_" ) );
                ret |= fprintf( f, " {Lib=%s}", CONV_TO_UTF8( Line ) );
                field = comp->m_ChipName;
                field.Replace( wxT( " " ), wxT( "_" ) );
                ret |= fprintf( f, " {Lib=%s}", CONV_TO_UTF8( field ) );
            }
            ret |= fprintf( f, "\n" );

@@ -803,7 +875,7 @@ static bool WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with
    {
        ret |= fprintf( f, "{ Pin List by Nets\n" );

        if( !WriteGENERICListOfNets( f, g_NetObjectslist ) )
        if( !WriteGENERICListOfNetsTxt( f, g_NetObjectslist ) )
            ret = -1;

        ret |= fprintf( f, "}\n" );
@@ -907,13 +979,14 @@ static void EraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList )


/**
 * Used for multiple parts per package components.
 * Function FindAllInstancesOfComponent
 * is used for multiple parts per package components.
 *
 * Search all instances of Component_in,
 * Calls AddPinToComponentPinList() to and pins founds to the current
 * component pin list
 */
static void FindAllsInstancesOfComponent( SCH_COMPONENT*  Component_in,
static void FindAllInstancesOfComponent( SCH_COMPONENT*  Component_in,
                                          LIB_COMPONENT*  aEntry,
                                          SCH_SHEET_PATH* Sheet_in )
{
@@ -981,7 +1054,7 @@ static bool SortPinsByNum( NETLIST_OBJECT* Pin1, NETLIST_OBJECT* Pin2 )
/* Written in the file / net list (ranked by Netcode), and elements that are
 * connected
 */
static bool WriteGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
static bool WriteGENERICListOfNetsTxt( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
{
    int         ret = 0;
    int         netCode;
@@ -1067,6 +1140,91 @@ static bool WriteGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
}


/**
 * Function WriteGENERICListOfNets
 * saves a netlist in xml format.
 */
static bool WriteGENERICListOfNets( wxXmlNode* aNode, NETLIST_OBJECT_LIST& aObjectsList )
{
    wxString    netCodeTxt;
    wxString    netName;
    wxString    ref;

    wxString    sNet  = wxT( "net" );
    wxString    sName = wxT( "name" );
    wxString    sCode = wxT( "code" );
    wxString    sRef  = wxT( "ref" );
    wxString    sPin  = wxT( "pin" );
    wxString    sNode = wxT( "node" );
    wxString    sFmtd = wxT( "%d" );

    wxXmlNode*  xnet;
    int         netCode;
    int         lastNetCode = -1;
    int         sameNetcodeCount = 0;

    /*  output:
        <net code="123" name="/cfcard.sch/WAIT#">
            <node ref="R23" pin="1"/>
            <node ref="U18" pin="12"/>
        </net>
    */

    for( unsigned ii = 0; ii < aObjectsList.size(); ii++ )
    {
        SCH_COMPONENT*  comp;

        // New net found, write net id;
        if( ( netCode = aObjectsList[ii]->GetNet() ) != lastNetCode )
        {
            sameNetcodeCount = 0;   // item count for this net

            netName.Empty();

            // Find a label for this net, if it exists.
            NETLIST_OBJECT* netref = aObjectsList[ii]->m_NetNameCandidate;
            if( netref )
            {
                if( netref->m_Type != NET_PINLABEL && netref->m_Type != NET_GLOBLABEL )
                {
                    // usual net name, prefix it by the sheet path
                    netName = netref->m_SheetList.PathHumanReadable();
                }

                netName += netref->m_Label;
            }

            lastNetCode  = netCode;
        }

        if( aObjectsList[ii]->m_Type != NET_PIN )
            continue;

        comp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link;

        // Get the reference for the net name and the main parent component
        ref = comp->GetRef( &aObjectsList[ii]->m_SheetList );
        if( ref[0] == wxChar( '#' ) )
            continue;

        if( ++sameNetcodeCount == 1 )
        {
            aNode->AddChild( xnet = Node( sNet ) );
            netCodeTxt.Printf( sFmtd, netCode );
            xnet->AddProperty( sCode, netCodeTxt );
            xnet->AddProperty( sName, netName );
        }

        wxXmlNode*  xnode;
        xnet->AddChild( xnode = Node( sNode ) );
        xnode->AddProperty( sRef, ref );
        xnode->AddProperty( sPin,  LIB_PIN::ReturnPinStringNum( aObjectsList[ii]->m_PinNum ) );
    }

    return true;
}


/* Generate CADSTAR net list. */
wxString StartLine( wxT( "." ) );