1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 1992-2018 jp.charras at wanadoo.fr
5  * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 #include <build_version.h>
27 #include <confirm.h>
28 
29 #include <connection_graph.h>
30 #include <string_utils.h>
31 #include <sch_edit_frame.h>
32 #include <sch_reference_list.h>
33 
34 #include "netlist_exporter_cadstar.h"
35 
36 /* Generate CADSTAR net list. */
37 static wxString StartLine( wxT( "." ) );
38 
WriteNetlist(const wxString & aOutFileName,unsigned)39 bool NETLIST_EXPORTER_CADSTAR::WriteNetlist( const wxString& aOutFileName,
40                                              unsigned /* aNetlistOptions */ )
41 {
42     int ret = 0;
43     FILE* f = nullptr;
44 
45     if( ( f = wxFopen( aOutFileName, wxT( "wt" ) ) ) == nullptr )
46     {
47         wxString msg;
48         msg.Printf( _( "Failed to create file '%s'." ), aOutFileName );
49         DisplayError( nullptr, msg );
50         return false;
51     }
52 
53     wxString StartCmpDesc = StartLine + wxT( "ADD_COM" );
54     wxString msg;
55     wxString footprint;
56     SCH_SYMBOL* symbol;
57     wxString title = wxT( "Eeschema " ) + GetBuildVersion();
58 
59     ret |= fprintf( f, "%sHEA\n", TO_UTF8( StartLine ) );
60     ret |= fprintf( f, "%sTIM %s\n", TO_UTF8( StartLine ), TO_UTF8( DateAndTime() ) );
61     ret |= fprintf( f, "%sAPP ", TO_UTF8( StartLine ) );
62     ret |= fprintf( f, "\"%s\"\n", TO_UTF8( title ) );
63     ret |= fprintf( f, ".TYP FULL\n\n" );
64 
65     // Create netlist footprints section
66     m_referencesAlreadyFound.Clear();
67 
68     SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
69 
70     for( unsigned i = 0; i < sheetList.size(); i++ )
71     {
72         for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
73         {
74             symbol = findNextSymbol( item, &sheetList[ i ] );
75 
76             if( !symbol )
77                 continue;
78 
79             if( !symbol->GetField( FOOTPRINT_FIELD )->IsVoid() )
80                 footprint = symbol->GetField( FOOTPRINT_FIELD )->GetShownText();
81             else
82                 footprint = "$noname";
83 
84             msg = symbol->GetRef( &sheetList[i] );
85             ret |= fprintf( f, "%s     ", TO_UTF8( StartCmpDesc ) );
86             ret |= fprintf( f, "%s", TO_UTF8( msg ) );
87 
88             msg = symbol->GetValue( &sheetList[i], true );
89             msg.Replace( wxT( " " ), wxT( "_" ) );
90             ret |= fprintf( f, "     \"%s\"", TO_UTF8( msg ) );
91             ret |= fprintf( f, "     \"%s\"", TO_UTF8( footprint ) );
92             ret |= fprintf( f, "\n" );
93         }
94     }
95 
96     ret |= fprintf( f, "\n" );
97 
98     if( ! writeListOfNets( f ) )
99         ret = -1;   // set error
100 
101     ret |= fprintf( f, "\n%sEND\n", TO_UTF8( StartLine ) );
102 
103     fclose( f );
104 
105     return ret >= 0;
106 }
107 
108 
writeListOfNets(FILE * f)109 bool NETLIST_EXPORTER_CADSTAR::writeListOfNets( FILE* f )
110 {
111     int ret       = 0;
112     int print_ter = 0;
113 
114     wxString InitNetDesc  = StartLine + wxT( "ADD_TER" );
115     wxString StartNetDesc = StartLine + wxT( "TER" );
116     wxString InitNetDescLine;
117     wxString netName;
118 
119     for( const auto& it : m_schematic->ConnectionGraph()->GetNetMap() )
120     {
121         auto subgraphs = it.second;
122 
123         netName.Printf( wxT( "\"%s\"" ), it.first.first );
124 
125         std::vector<std::pair<SCH_PIN*, SCH_SHEET_PATH>> sorted_items;
126 
127         for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
128         {
129             SCH_SHEET_PATH sheet = subgraph->m_sheet;
130 
131             for( SCH_ITEM* item : subgraph->m_items )
132             {
133                 if( item->Type() == SCH_PIN_T )
134                     sorted_items.emplace_back( static_cast<SCH_PIN*>( item ), sheet );
135             }
136         }
137 
138         // Netlist ordering: Net name, then ref des, then pin name
139         std::sort( sorted_items.begin(), sorted_items.end(),
140                 []( std::pair<SCH_PIN*, SCH_SHEET_PATH> a, std::pair<SCH_PIN*, SCH_SHEET_PATH> b )
141                 {
142                     wxString ref_a = a.first->GetParentSymbol()->GetRef( &a.second );
143                     wxString ref_b = b.first->GetParentSymbol()->GetRef( &b.second );
144 
145                     if( ref_a == ref_b )
146                         return a.first->GetShownNumber() < b.first->GetShownNumber();
147 
148                     return ref_a < ref_b;
149                 } );
150 
151         // Some duplicates can exist, for example on multi-unit parts with duplicated
152         // pins across units.  If the user connects the pins on each unit, they will
153         // appear on separate subgraphs.  Remove those here:
154         sorted_items.erase( std::unique( sorted_items.begin(), sorted_items.end(),
155                 []( std::pair<SCH_PIN*, SCH_SHEET_PATH> a, std::pair<SCH_PIN*, SCH_SHEET_PATH> b )
156                 {
157                     wxString ref_a = a.first->GetParentSymbol()->GetRef( &a.second );
158                     wxString ref_b = b.first->GetParentSymbol()->GetRef( &b.second );
159 
160                     return ref_a == ref_b && a.first->GetShownNumber() == b.first->GetShownNumber();
161                 } ),
162                 sorted_items.end() );
163 
164         print_ter = 0;
165 
166         for( const std::pair<SCH_PIN*, SCH_SHEET_PATH>& pair : sorted_items )
167         {
168             SCH_PIN*       pin   = pair.first;
169             SCH_SHEET_PATH sheet = pair.second;
170 
171             wxString refText = pin->GetParentSymbol()->GetRef( &sheet );
172             wxString pinText = pin->GetShownNumber();
173 
174             // Skip power symbols and virtual symbols
175             if( refText[0] == wxChar( '#' ) )
176                 continue;
177 
178             switch( print_ter )
179             {
180             case 0:
181                 InitNetDescLine.Printf( wxT( "\n%s   %s   %.4s     %s" ),
182                                         InitNetDesc,
183                                         refText,
184                                         pinText,
185                                         netName );
186                 print_ter++;
187                 break;
188 
189             case 1:
190                 ret |= fprintf( f, "%s\n", TO_UTF8( InitNetDescLine ) );
191                 ret |= fprintf( f, "%s       %s   %.4s\n",
192                                 TO_UTF8( StartNetDesc ),
193                                 TO_UTF8( refText ),
194                                 TO_UTF8( pinText ) );
195                 print_ter++;
196                 break;
197 
198             default:
199                 ret |= fprintf( f, "            %s   %.4s\n",
200                                 TO_UTF8( refText ),
201                                 TO_UTF8( pinText ) );
202                 break;
203             }
204         }
205     }
206 
207     return ret >= 0;
208 }
209