Commit 90c130b6 authored by jean-pierre charras's avatar jean-pierre charras
Browse files

Eeschema: intermediate netlist : fix duplicate pins in list of nets, with multi parts per package.

parent 0cb6cd8c
Loading
Loading
Loading
Loading
+73 −66
Original line number Original line Diff line number Diff line
@@ -127,7 +127,7 @@ class EXPORT_HELP
     *  <ul>
     *  <ul>
     * <li> "?" if pin not connected
     * <li> "?" if pin not connected
     * <li> "netname" for global net (like gnd, vcc ..
     * <li> "netname" for global net (like gnd, vcc ..
     * <li> "netname_sheetnumber" for the usual nets
     * <li> "/path/netname" for the usual nets
     * </ul>
     * </ul>
     */
     */
    static void sprintPinNetName( wxString* aResult, const wxString& aNetNameFormat, NETLIST_OBJECT* aPin );
    static void sprintPinNetName( wxString* aResult, const wxString& aNetNameFormat, NETLIST_OBJECT* aPin );
@@ -135,11 +135,11 @@ class EXPORT_HELP
    /**
    /**
     * Function findNextComponentAndCreatePinList
     * Function findNextComponentAndCreatePinList
     * finds a "suitable" component from the DrawList and optionally builds
     * finds a "suitable" component from the DrawList and optionally builds
     * its pin list int m_SortedComponentPinList. The list is sorted by pin num.
     * its pin list in m_SortedComponentPinList. The list is sorted by pin num.
     * A suitable component is a "new" real component (power symbols are not
     * A suitable component is a "new" real component
     * considered).
     * (power symbols and virtual components that have their reference starting by '#'are skipped).
     */
     */
    SCH_COMPONENT* findNextComponentAndCreatPinList( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );
    SCH_COMPONENT* findNextComponentAndCreatePinList( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );


    SCH_COMPONENT* findNextComponent( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );
    SCH_COMPONENT* findNextComponent( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );


@@ -158,9 +158,9 @@ class EXPORT_HELP
    void eraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList );
    void eraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList );


    /**
    /**
     * Function addPintToComponentPinList
     * Function addPinToComponentPinList
     * adds a new pin description to the pin list m_SortedComponentPinList.
     * adds a new pin description to the pin list m_SortedComponentPinList.
     * A.a pin description is a pointer to the corresponding structure
     * A pin description is a pointer to the corresponding structure
     * created by BuildNetList() in the table g_NetObjectslist.
     * created by BuildNetList() in the table g_NetObjectslist.
     */
     */
    bool addPinToComponentPinList( SCH_COMPONENT*  Component,
    bool addPinToComponentPinList( SCH_COMPONENT*  Component,
@@ -242,22 +242,20 @@ public:
    /**
    /**
     * Function WriteGENERICNetList
     * Function WriteGENERICNetList
     * creates a generic netlist, now in XML.
     * creates a generic netlist, now in XML.
     * @param frame = the parent SCH_EDIT_FRAME frame
     * @param aOutFileName = the full filename of the file to create
     * @param aOutFileName = the full filename of the file to create
     * @return bool - true if there were no errors, else false.
     * @return bool - true if there were no errors, else false.
     */
     */
    bool WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aOutFileName );
    bool WriteGENERICNetList( const wxString& aOutFileName );


    /**
    /**
     * Function WriteNetListPCBNEW
     * Function WriteNetListPCBNEW
     * generates a net list file (Format 2 improves ORCAD PCB)
     * generates a net list file (Format 2 improves ORCAD PCB)
     *
     *
     * @param frame = the parent SCH_EDIT_FRAME frame
     * @param f = the file to write to
     * @param f = the file to write to
     * @param with_pcbnew if true, then format Pcbnew (OrcadPcb2 + reviews and lists of net),<p>
     * @param with_pcbnew if true, then use Pcbnew format (OrcadPcb2 + a list of net),<p>
     *                    else output ORCADPCB2 strict format.
     *                    else use ORCADPCB2 basic format.
     */
     */
    bool WriteNetListPCBNEW( SCH_EDIT_FRAME* frame, FILE* f, bool with_pcbnew );
    bool WriteNetListPCBNEW( FILE* f, bool with_pcbnew );


    /**
    /**
     * Function WriteNetListCADSTAR
     * Function WriteNetListCADSTAR
@@ -287,22 +285,21 @@ public:
     * .. B * T3 1
     * .. B * T3 1
     *U1 * 14
     *U1 * 14
     */
     */
    void WriteNetListCADSTAR( SCH_EDIT_FRAME* frame, FILE* f );
    void WriteNetListCADSTAR( FILE* f );


    /**
    /**
     * Function WriteNetListPspice
     * Function WriteNetListPspice
     * generates a netlist file in PSPICE format.
     * generates a netlist file in PSPICE format.
     * <p>
     * <p>
     * All graphics text commentary by a [.-+] PSpice or [.-+] gnucap
     * All graphics text starting by  [.-+] PSpice or [.-+] gnucap
     * Are considered in placing orders in the netlist
     * are seen as spice directives and put in netlist
     * [.-] Or PSpice gnucap are beginning
     * .-PSpice or .-gnucap put at beginning of the netlist
     * + + Gnucap and PSpice are ultimately NetList
     * .+PSpice or .-genucap are put at end of the netList
     * @param frame = the parent SCH_EDIT_FRAME frame
     * @param f = the file to write to
     * @param f = the file to write to
     * @param use_netnames if true, then nodes are identified by the netname,
     * @param use_netnames = true, to use netnames in netlist,
     *          else by net number.
     *                      false to use net number.
     */
     */
    bool WriteNetListPspice( SCH_EDIT_FRAME* frame, FILE* f, bool use_netnames );
    bool WriteNetListPspice( FILE* f, bool use_netnames );


    /**
    /**
     * Function MakeCommandLine
     * Function MakeCommandLine
@@ -385,22 +382,22 @@ bool SCH_EDIT_FRAME::WriteNetListFile( int aFormat, const wxString& aFullFileNam
    switch( aFormat )
    switch( aFormat )
    {
    {
    case NET_TYPE_PCBNEW:
    case NET_TYPE_PCBNEW:
        ret = helper.WriteNetListPCBNEW( this, f, true );
        ret = helper.WriteNetListPCBNEW( f, true );
        fclose( f );
        fclose( f );
        break;
        break;


    case NET_TYPE_ORCADPCB2:
    case NET_TYPE_ORCADPCB2:
        ret = helper.WriteNetListPCBNEW( this, f, false );
        ret = helper.WriteNetListPCBNEW( f, false );
        fclose( f );
        fclose( f );
        break;
        break;


    case NET_TYPE_CADSTAR:
    case NET_TYPE_CADSTAR:
        helper.WriteNetListCADSTAR( this, f );
        helper.WriteNetListCADSTAR( f );
        fclose( f );
        fclose( f );
        break;
        break;


    case NET_TYPE_SPICE:
    case NET_TYPE_SPICE:
        ret = helper.WriteNetListPspice( this, f, aUse_netnames );
        ret = helper.WriteNetListPspice( f, aUse_netnames );
        fclose( f );
        fclose( f );
        break;
        break;


@@ -411,7 +408,7 @@ bool SCH_EDIT_FRAME::WriteNetListFile( int aFormat, const wxString& aFullFileNam


            D(printf("tmpFile:'%s'\n", CONV_TO_UTF8( tmpFile.GetFullPath() ) );)
            D(printf("tmpFile:'%s'\n", CONV_TO_UTF8( tmpFile.GetFullPath() ) );)


            ret = helper.WriteGENERICNetList( this, tmpFile.GetFullPath() );
            ret = helper.WriteGENERICNetList( tmpFile.GetFullPath() );
            if( !ret )
            if( !ret )
                break;
                break;


@@ -542,7 +539,7 @@ SCH_COMPONENT* EXPORT_HELP::findNextComponent( EDA_ITEM* aItem, SCH_SHEET_PATH*
}
}




SCH_COMPONENT* EXPORT_HELP::findNextComponentAndCreatPinList( EDA_ITEM*       aItem,
SCH_COMPONENT* EXPORT_HELP::findNextComponentAndCreatePinList( EDA_ITEM*       aItem,
                                                              SCH_SHEET_PATH* aSheetPath )
                                                              SCH_SHEET_PATH* aSheetPath )
{
{
    wxString    ref;
    wxString    ref;
@@ -863,6 +860,9 @@ XNODE* EXPORT_HELP::makeGenericListOfNets()
        if( nitem->m_Type != NET_PIN )
        if( nitem->m_Type != NET_PIN )
            continue;
            continue;


        if( nitem->m_Flag != 0 )     // Redundant pin, skip it
            continue;

        comp = (SCH_COMPONENT*) nitem->m_Link;
        comp = (SCH_COMPONENT*) nitem->m_Link;


        // Get the reference for the net name and the main parent component
        // Get the reference for the net name and the main parent component
@@ -953,7 +953,7 @@ XNODE* EXPORT_HELP::makeGenericComponents()
    {
    {
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        {
        {
            SCH_COMPONENT*  comp = findNextComponent( schItem, path );
            SCH_COMPONENT*  comp = findNextComponentAndCreatePinList( schItem, path );
            if( !comp )
            if( !comp )
                break;  // No component left
                break;  // No component left


@@ -1025,8 +1025,12 @@ XNODE* EXPORT_HELP::makeGenericComponents()


#include <wx/wfstream.h>        // wxFFileOutputStream
#include <wx/wfstream.h>        // wxFFileOutputStream


bool EXPORT_HELP::WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aOutFileName )
bool EXPORT_HELP::WriteGENERICNetList( const wxString& aOutFileName )
{
{
    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

#if 0
#if 0


    // this code seems to work now, for S-expression support.
    // this code seems to work now, for S-expression support.
@@ -1038,7 +1042,7 @@ bool EXPORT_HELP::WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aO
    {
    {
    L_error:
    L_error:
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        DisplayError( frame, msg );
        DisplayError( NULL, msg );
    }
    }
    else
    else
    {
    {
@@ -1080,7 +1084,7 @@ bool EXPORT_HELP::WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aO
    if( ( out = wxFopen( aOutFileName, wxT( "wt" ) ) ) == NULL )
    if( ( out = wxFopen( aOutFileName, wxT( "wt" ) ) ) == NULL )
    {
    {
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        DisplayError( frame, msg );
        DisplayError( NULL, msg );
        return false;
        return false;
    }
    }


@@ -1097,7 +1101,7 @@ bool EXPORT_HELP::WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aO
    {
    {
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        {
        {
            SCH_COMPONENT*  comp = findNextComponentAndCreatPinList( schItem, path );
            SCH_COMPONENT*  comp = findNextComponentAndCreatePinList( schItem, path );
            if( !comp )
            if( !comp )
                break;  // No component left
                break;  // No component left


@@ -1163,7 +1167,7 @@ bool EXPORT_HELP::WriteGENERICNetList( SCH_EDIT_FRAME* frame, const wxString& aO
}
}




bool EXPORT_HELP::WriteNetListPspice( SCH_EDIT_FRAME* frame, FILE* f, bool use_netnames )
bool EXPORT_HELP::WriteNetListPspice( FILE* f, bool use_netnames )
{
{
    int             ret = 0;
    int             ret = 0;
    char            Line[1024];
    char            Line[1024];
@@ -1181,6 +1185,10 @@ bool EXPORT_HELP::WriteNetListPspice( SCH_EDIT_FRAME* frame, FILE* f, bool use_n


    ret |= fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, Line );
    ret |= fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, Line );


    // Prepare list of nets generation (not used here, but...
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

    // Create text list starting by [.-]pspice , or [.-]gnucap (simulator
    // Create text list starting by [.-]pspice , or [.-]gnucap (simulator
    // commands) and create text list starting by [+]pspice , or [+]gnucap
    // commands) and create text list starting by [+]pspice , or [+]gnucap
    // (simulator commands)
    // (simulator commands)
@@ -1255,7 +1263,7 @@ bool EXPORT_HELP::WriteNetListPspice( SCH_EDIT_FRAME* frame, FILE* f, bool use_n
    {
    {
        for( EDA_ITEM* item = sheet->LastDrawList();  item;  item = item->Next() )
        for( EDA_ITEM* item = sheet->LastDrawList();  item;  item = item->Next() )
        {
        {
            SCH_COMPONENT* comp = findNextComponentAndCreatPinList( item, sheet );
            SCH_COMPONENT* comp = findNextComponentAndCreatePinList( item, sheet );
            if( !comp )
            if( !comp )
                break;
                break;


@@ -1316,7 +1324,7 @@ bool EXPORT_HELP::WriteNetListPspice( SCH_EDIT_FRAME* frame, FILE* f, bool use_n
}
}




bool EXPORT_HELP::WriteNetListPCBNEW( SCH_EDIT_FRAME* frame, FILE* f, bool with_pcbnew )
bool EXPORT_HELP::WriteNetListPCBNEW( FILE* f, bool with_pcbnew )
{
{
    wxString    field;
    wxString    field;
    wxString    footprint;
    wxString    footprint;
@@ -1333,8 +1341,11 @@ bool EXPORT_HELP::WriteNetListPCBNEW( SCH_EDIT_FRAME* frame, FILE* f, bool with_
    else
    else
        ret |= fprintf( f, "( { %s created  %s }\n", NETLIST_HEAD_STRING, dateBuf );
        ret |= fprintf( f, "( { %s created  %s }\n", NETLIST_HEAD_STRING, dateBuf );


    // Create netlist module section
    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;


    // Create netlist module section
    m_ReferencesAlreadyFound.Clear();
    m_ReferencesAlreadyFound.Clear();


    SCH_SHEET_LIST sheetList;
    SCH_SHEET_LIST sheetList;
@@ -1343,7 +1354,7 @@ bool EXPORT_HELP::WriteNetListPCBNEW( SCH_EDIT_FRAME* frame, FILE* f, bool with_
    {
    {
        for( EDA_ITEM* item = path->LastDrawList();  item;  item = item->Next() )
        for( EDA_ITEM* item = path->LastDrawList();  item;  item = item->Next() )
        {
        {
            SCH_COMPONENT* comp = findNextComponentAndCreatPinList( item, path );
            SCH_COMPONENT* comp = findNextComponentAndCreatePinList( item, path );


            if( !comp )
            if( !comp )
                break;
                break;
@@ -1494,7 +1505,11 @@ bool EXPORT_HELP::addPinToComponentPinList( SCH_COMPONENT* aComponent,
    return false;
    return false;
}
}



/*
 * remove duplicate pins from aPinList (list of pins relative to a given component)
 * (i.e. set pointer to duplicate pins to NULL in this list).
 * also set .m_Flag member of "removed" NETLIST_OBJECT pins to 1
 */
void EXPORT_HELP::eraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList )
void EXPORT_HELP::eraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList )
{
{
    if( aPinList.size() == 0 )  // Trivial case: component with no pin
    if( aPinList.size() == 0 )  // Trivial case: component with no pin
@@ -1519,27 +1534,34 @@ void EXPORT_HELP::eraseDuplicatePins( NETLIST_OBJECT_LIST& aPinList )
            if(  aPinList[jj] == NULL )   // Already removed
            if(  aPinList[jj] == NULL )   // Already removed
                continue;
                continue;


            // other pin num end of duplicate list.
            // if other pin num, stop search,
            // because all pins having the same number are consecutive in list.
            if( aPinList[idxref]->m_PinNum != aPinList[jj]->m_PinNum )
            if( aPinList[idxref]->m_PinNum != aPinList[jj]->m_PinNum )
                break;
                break;


            if( aPinList[idxref]->m_FlagOfConnection == PAD_CONNECT )
            if( aPinList[idxref]->m_FlagOfConnection == PAD_CONNECT )
            {
                aPinList[jj]->m_Flag = 1;
                aPinList[jj] = NULL;
                aPinList[jj] = NULL;
            }
            else /* the reference pin is not connected: remove this pin if the
            else /* the reference pin is not connected: remove this pin if the
                  * other pin is connected */
                  * other pin is connected */
            {
            {
                if( aPinList[jj]->m_FlagOfConnection == PAD_CONNECT )
                if( aPinList[jj]->m_FlagOfConnection == PAD_CONNECT )
                {
                {
                    aPinList[idxref]->m_Flag = 1;
                    aPinList[idxref] = NULL;
                    aPinList[idxref] = NULL;
                    idxref = jj;
                    idxref = jj;
                }
                }
                else    // the 2 pins are not connected: remove the tested pin,
                else    // the 2 pins are not connected: remove the tested pin,
                        // and continue ...
                {       // and continue ...
                    aPinList[jj]->m_Flag = 1;
                    aPinList[jj] = NULL;
                    aPinList[jj] = NULL;
                }
                }
            }
            }
        }
        }
    }
    }
}




void EXPORT_HELP::findAllInstancesOfComponent( SCH_COMPONENT*  aComponent,
void EXPORT_HELP::findAllInstancesOfComponent( SCH_COMPONENT*  aComponent,
@@ -1634,6 +1656,9 @@ bool EXPORT_HELP::writeGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjects
        if( aObjectsList[ii]->m_Type != NET_PIN )
        if( aObjectsList[ii]->m_Type != NET_PIN )
            continue;
            continue;


        if( aObjectsList[ii]->m_Flag != 0 )     // Redundant pin, skip it
            continue;

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


        // Get the reference for the net name and the main parent component
        // Get the reference for the net name and the main parent component
@@ -1674,7 +1699,7 @@ bool EXPORT_HELP::writeGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjects
static wxString StartLine( wxT( "." ) );
static wxString StartLine( wxT( "." ) );




void EXPORT_HELP::WriteNetListCADSTAR( SCH_EDIT_FRAME* frame, FILE* f )
void EXPORT_HELP::WriteNetListCADSTAR( FILE* f )
{
{
    wxString StartCmpDesc = StartLine + wxT( "ADD_COM" );
    wxString StartCmpDesc = StartLine + wxT( "ADD_COM" );
    wxString msg;
    wxString msg;
@@ -1692,6 +1717,10 @@ void EXPORT_HELP::WriteNetListCADSTAR( SCH_EDIT_FRAME* frame, FILE* f )
    fprintf( f, "\"%s\"\n", CONV_TO_UTF8( Title ) );
    fprintf( f, "\"%s\"\n", CONV_TO_UTF8( Title ) );
    fprintf( f, "\n" );
    fprintf( f, "\n" );


    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

    // Create netlist module section
    // Create netlist module section
    m_ReferencesAlreadyFound.Clear();
    m_ReferencesAlreadyFound.Clear();


@@ -1701,7 +1730,7 @@ void EXPORT_HELP::WriteNetListCADSTAR( SCH_EDIT_FRAME* frame, FILE* f )
    {
    {
        for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() )
        for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() )
        {
        {
            DrawList = Component = findNextComponentAndCreatPinList( DrawList, sheet );
            DrawList = Component = findNextComponentAndCreatePinList( DrawList, sheet );
            if( Component == NULL )
            if( Component == NULL )
                break;
                break;


@@ -1748,9 +1777,6 @@ void EXPORT_HELP::writeListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjects
    SCH_COMPONENT* Cmp;
    SCH_COMPONENT* Cmp;
    wxString NetName;
    wxString NetName;


    for( ii = 0; ii < aObjectsList.size(); ii++ )
        aObjectsList[ii]->m_Flag = 0;

    for( ii = 0; ii < g_NetObjectslist.size(); ii++ )
    for( ii = 0; ii < g_NetObjectslist.size(); ii++ )
    {
    {
        // Get the NetName of the current net :
        // Get the NetName of the current net :
@@ -1829,24 +1855,5 @@ void EXPORT_HELP::writeListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjects
        }
        }


        aObjectsList[ii]->m_Flag = 1;
        aObjectsList[ii]->m_Flag = 1;

        // Search for redundant pins to avoid generation of the same connection
        // more than once.
        for( unsigned jj = ii + 1; jj < aObjectsList.size(); jj++ )
        {
            if( aObjectsList[jj]->GetNet() != NetCode )
                break;
            if( aObjectsList[jj]->m_Type != NET_PIN )
                continue;
            SCH_COMPONENT* tstcmp =
                (SCH_COMPONENT*) aObjectsList[jj]->m_Link;
            wxString p    = Cmp->GetPath( &( aObjectsList[ii]->m_SheetList ) );
            wxString tstp = tstcmp->GetPath( &( aObjectsList[jj]->m_SheetList ) );
            if( p.Cmp( tstp ) != 0 )
                continue;

            if( aObjectsList[jj]->m_PinNum == aObjectsList[ii]->m_PinNum )
                aObjectsList[jj]->m_Flag = 1;
        }
    }
    }
}
}
+4 −4
Original line number Original line Diff line number Diff line
@@ -126,10 +126,10 @@
            <pin num="1" type="passive"/>
            <pin num="1" type="passive"/>
            <pin num="2" type="passive"/>
            <pin num="2" type="passive"/>
          </pins>
          </pins>
    Output pin list is ( pin num = net name )
    Output pin list is ( <pin num> <net name> )
    something like
    something like
            ( 1 = VCC )
            ( 1 VCC )
            ( 2 = GND )
            ( 2 GND )
-->
-->
<xsl:template name="build_pin_list" match="pin">
<xsl:template name="build_pin_list" match="pin">
    <xsl:param name="cmp_ref" select="0" />
    <xsl:param name="cmp_ref" select="0" />
@@ -137,7 +137,7 @@
    <!-- write pin numner and separator -->
    <!-- write pin numner and separator -->
    <xsl:text>  ( </xsl:text>
    <xsl:text>  ( </xsl:text>
    <xsl:value-of select="@num"/>
    <xsl:value-of select="@num"/>
    <xsl:text> = </xsl:text>
    <xsl:text> </xsl:text>


    <!-- search net name in nets section and write it: -->
    <!-- search net name in nets section and write it: -->
    <xsl:variable name="pinNum" select="@num" />
    <xsl:variable name="pinNum" select="@num" />