KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_easyeda_plugin.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Alex Shvartzkop <[email protected]>
5 * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include "sch_easyeda_plugin.h"
26#include "sch_easyeda_parser.h"
27
28#include <schematic.h>
29#include <sch_sheet.h>
30#include <sch_screen.h>
32#include <project_sch.h>
33
34#include <wx/log.h>
35#include <wx/stdstream.h>
36#include <wx/zipstrm.h>
37
38#include <nlohmann/json.hpp>
39#include <core/map_helpers.h>
40#include <wx/wfstream.h>
41
42
43
44const wxString SCH_EASYEDA_PLUGIN::GetName() const
45{
46 return wxT( "EasyEDA (JLCEDA) Schematic Importer" );
47}
48
49
50static bool FindSchFileInStream( const wxString& aName, wxInputStream& aStream,
51 nlohmann::json& aOut, EASYEDA::DOCUMENT& aDoc,
52 EASYEDA::DOC_TYPE& aDocType )
53{
54 if( aName.Lower().EndsWith( wxS( ".json" ) ) )
55 {
56 wxStdInputStream sin( aStream );
57 nlohmann::json js = nlohmann::json::parse( sin, nullptr, false );
58
59 if( js.is_discarded() )
60 return false;
61
64
65 if( doc.docType )
66 type = *doc.docType;
67 else
68 type = doc.head.docType;
69
71 || type == EASYEDA::DOC_TYPE::SYMBOL )
72 {
73 aOut = js;
74 aDoc = doc;
75 aDocType = type;
76 return true;
77 }
78 }
79 else if( aName.Lower().EndsWith( wxS( ".zip" ) ) )
80 {
81 std::shared_ptr<wxZipEntry> entry;
82 wxZipInputStream zip( aStream );
83
84 if( !zip.IsOk() )
85 return false;
86
87 while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
88 {
89 wxString name = entry->GetName();
90
91 if( FindSchFileInStream( name, zip, aOut, aDoc, aDocType ) )
92 return true;
93 }
94 }
95
96 return false;
97}
98
99
100bool SCH_EASYEDA_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
101{
102 if( !SCH_PLUGIN::CanReadSchematicFile( aFileName ) )
103 return false;
104
105 try
106 {
107 wxFFileInputStream in( aFileName );
108 nlohmann::json js;
110 EASYEDA::DOC_TYPE docType;
111
112 return FindSchFileInStream( aFileName, in, js, doc, docType );
113 }
114 catch( nlohmann::json::exception& )
115 {
116 }
117 catch( std::exception& )
118 {
119 }
120
121 return false;
122}
123
124
125bool SCH_EASYEDA_PLUGIN::CanReadLibrary( const wxString& aFileName ) const
126{
127 return CanReadSchematicFile( aFileName );
128}
129
130
132{
133 return 0;
134}
135
136
137LIB_SYMBOL* loadSymbol( const wxString& aLibraryPath, nlohmann::json aFileData,
138 const wxString& aAliasName, const STRING_UTF8_MAP* aProperties )
139{
140 SCH_EASYEDA_PARSER parser( nullptr, nullptr );
141 std::map<wxString, int> namesCounter;
142
143 try
144 {
145 wxFFileInputStream in( aLibraryPath );
146 nlohmann::json js;
147 EASYEDA::DOCUMENT topDoc;
148 EASYEDA::DOC_TYPE topDocType;
149
150 if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
151 {
152 THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
153 aLibraryPath ) );
154 }
155
156 if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
157 || topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
158 {
160
161 for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
162 {
163 if( subDoc.docType )
164 {
166 continue;
167 }
168 else
169 {
171 continue;
172 }
173
174 EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
175
176 for( wxString shap : dataStrDoc.shape )
177 {
178 if( !shap.Contains( wxS( "LIB" ) ) )
179 continue;
180
181 shap.Replace( wxS( "#@$" ), wxS( "\n" ) );
182 wxArrayString parts = wxSplit( shap, '\n', '\0' );
183
184 if( parts.size() < 1 )
185 continue;
186
187 wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
188
189 if( paramsRoot.size() < 1 )
190 continue;
191
192 wxString rootType = paramsRoot[0];
193
194 if( rootType == wxS( "LIB" ) )
195 {
196 if( paramsRoot.size() < 4 )
197 continue;
198
199 VECTOR2D origin( parser.Convert( paramsRoot[1] ),
200 parser.Convert( paramsRoot[2] ) );
201
202 wxString symbolName = wxString::Format( wxS( "Unknown_%s_%s" ),
203 paramsRoot[1], paramsRoot[2] );
204
205 wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
206
207 std::map<wxString, wxString> paramMap;
208
209 for( size_t i = 1; i < paramParts.size(); i += 2 )
210 {
211 wxString key = paramParts[i - 1];
212 wxString value = paramParts[i];
213
214 if( key == wxS( "spiceSymbolName" ) && !value.IsEmpty() )
215 symbolName = value;
216
217 paramMap[key] = value;
218 }
219
220 int& serial = namesCounter[symbolName];
221
222 if( serial > 0 )
223 symbolName << wxS( "_" ) << serial;
224
225 serial++;
226
227 paramMap[wxS( "spiceSymbolName" )] = symbolName;
228
229 if( symbolName == aAliasName )
230 {
231 parts.RemoveAt( 0 );
232
233 return parser.ParseSymbol( origin, paramMap, parts );
234 }
235 }
236 }
237 }
238 }
239 else if( topDocType == EASYEDA::DOC_TYPE::SYMBOL )
240 {
242
243 wxString symbolName = wxS( "Unknown" );
244
245 std::optional<std::map<wxString, wxString>> c_para;
246
247 if( symDoc.c_para )
248 c_para = symDoc.c_para;
249 else if( topDoc.head.c_para )
250 c_para = topDoc.head.c_para;
251
252 if( !c_para )
253 return nullptr;
254
255 symbolName = get_def( *c_para, wxS( "name" ), symbolName );
256
257 int& serial = namesCounter[symbolName];
258
259 if( serial > 0 )
260 symbolName << wxS( "_" ) << serial;
261
262 serial++;
263
264 if( symbolName != aAliasName )
265 return nullptr;
266
267 VECTOR2D origin( topDoc.head.x, topDoc.head.y );
268
269 return parser.ParseSymbol( origin, *c_para, topDoc.shape );
270 }
271 }
272 catch( nlohmann::json::exception& e )
273 {
274 THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
275 aAliasName, aLibraryPath, e.what() ) );
276 }
277 catch( std::exception& e )
278 {
279 THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
280 aAliasName, aLibraryPath, e.what() ) );
281 }
282
283 return nullptr;
284}
285
286
287void SCH_EASYEDA_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
288 const wxString& aLibraryPath,
289 const STRING_UTF8_MAP* aProperties )
290{
291 std::map<wxString, int> namesCounter;
292
293 try
294 {
295 wxFFileInputStream in( aLibraryPath );
296 nlohmann::json js;
297 EASYEDA::DOCUMENT topDoc;
298 EASYEDA::DOC_TYPE topDocType;
299
300 if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
301 {
302 THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
303 aLibraryPath ) );
304 }
305
306 if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
307 || topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
308 {
310
311 for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
312 {
313 if( subDoc.docType )
314 {
316 continue;
317 }
318 else
319 {
321 continue;
322 }
323
324 EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
325
326 for( wxString shap : dataStrDoc.shape )
327 {
328 if( !shap.Contains( wxS( "LIB" ) ) )
329 continue;
330
331 shap.Replace( wxS( "#@$" ), wxS( "\n" ) );
332 wxArrayString parts = wxSplit( shap, '\n', '\0' );
333
334 if( parts.size() < 1 )
335 continue;
336
337 wxArrayString paramsRoot = wxSplit( parts[0], '~', '\0' );
338
339 if( paramsRoot.size() < 1 )
340 continue;
341
342 wxString rootType = paramsRoot[0];
343
344 if( rootType == wxS( "LIB" ) )
345 {
346 if( paramsRoot.size() < 4 )
347 continue;
348
349 wxString symbolName = wxString::Format( wxS( "Unknown_%s_%s" ),
350 paramsRoot[1], paramsRoot[2] );
351
352 wxArrayString paramParts = wxSplit( paramsRoot[3], '`', '\0' );
353
354 std::map<wxString, wxString> paramMap;
355
356 for( size_t i = 1; i < paramParts.size(); i += 2 )
357 {
358 wxString key = paramParts[i - 1];
359 wxString value = paramParts[i];
360
361 if( key == wxS( "spiceSymbolName" ) && !value.IsEmpty() )
362 symbolName = value;
363
364 paramMap[key] = value;
365 }
366
367 int& serial = namesCounter[symbolName];
368
369 if( serial > 0 )
370 symbolName << wxS( "_" ) << serial;
371
372 serial++;
373
374 aSymbolNameList.Add( symbolName );
375 }
376 }
377 }
378 }
379 else if( topDocType == EASYEDA::DOC_TYPE::SYMBOL )
380 {
382
383 wxString packageName = wxS( "Unknown" );
384
385 if( symDoc.c_para )
386 {
387 packageName = get_def( *symDoc.c_para, wxS( "name" ), packageName );
388 }
389 else if( topDoc.head.c_para )
390 {
391 packageName = get_def( *topDoc.head.c_para, wxS( "name" ), packageName );
392 }
393
394 aSymbolNameList.Add( packageName );
395 }
396 }
397 catch( nlohmann::json::exception& e )
398 {
399 THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
400 aLibraryPath, e.what() ) );
401 }
402 catch( std::exception& e )
403 {
404 THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
405 aLibraryPath, e.what() ) );
406 }
407}
408
409
410void SCH_EASYEDA_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
411 const wxString& aLibraryPath,
412 const STRING_UTF8_MAP* aProperties )
413{
414 wxFFileInputStream in( aLibraryPath );
415 nlohmann::json js;
416 EASYEDA::DOCUMENT topDoc;
417 EASYEDA::DOC_TYPE topDocType;
418
419 if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
420 {
421 THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
422 aLibraryPath ) );
423 }
424
425 try
426 {
427 wxArrayString symbolNameList;
428
429 EnumerateSymbolLib( symbolNameList, aLibraryPath, aProperties );
430
431 for( const wxString& symbolName : symbolNameList )
432 {
433 LIB_SYMBOL* sym = loadSymbol( aLibraryPath, js, symbolName, aProperties );
434
435 if( sym )
436 aSymbolList.push_back( sym );
437 }
438 }
439 catch( nlohmann::json::exception& e )
440 {
441 THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
442 aLibraryPath, e.what() ) );
443 }
444 catch( std::exception& e )
445 {
446 THROW_IO_ERROR( wxString::Format( _( "Error enumerating symbol library '%s': %s" ),
447 aLibraryPath, e.what() ) );
448 }
449}
450
451
452LIB_SYMBOL* SCH_EASYEDA_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
453 const wxString& aAliasName,
454 const STRING_UTF8_MAP* aProperties )
455{
456 try
457 {
458 wxFFileInputStream in( aLibraryPath );
459 nlohmann::json js;
460 EASYEDA::DOCUMENT topDoc;
461 EASYEDA::DOC_TYPE topDocType;
462
463 if( !FindSchFileInStream( aLibraryPath, in, js, topDoc, topDocType ) )
464 {
465 THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
466 aLibraryPath ) );
467 }
468
469 return loadSymbol( aLibraryPath, js, aAliasName, aProperties );
470 }
471 catch( nlohmann::json::exception& e )
472 {
473 THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
474 aAliasName, aLibraryPath, e.what() ) );
475 }
476 catch( std::exception& e )
477 {
478 THROW_IO_ERROR( wxString::Format( _( "Error loading symbol '%s' from library '%s': %s" ),
479 aAliasName, aLibraryPath, e.what() ) );
480 }
481
482 return nullptr;
483}
484
485
486static void LoadSchematic( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const wxString& aFileName )
487{
488 SCH_EASYEDA_PARSER parser( nullptr, nullptr );
489
490 try
491 {
492 wxFFileInputStream in( aFileName );
493 nlohmann::json js;
494 EASYEDA::DOCUMENT topDoc;
495 EASYEDA::DOC_TYPE topDocType;
496
497 if( !FindSchFileInStream( aFileName, in, js, topDoc, topDocType ) )
498 {
499 THROW_IO_ERROR( wxString::Format( _( "Unable to find a valid schematic file in '%s'" ),
500 aFileName ) );
501 }
502
503 if( topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_SHEET
504 || topDocType == EASYEDA::DOC_TYPE::SCHEMATIC_LIST )
505 {
507
508 bool first = true;
509
510 for( const EASYEDA::DOCUMENT& subDoc : *schDoc.schematics )
511 {
512 if( first )
513 first = false; // TODO
514 else
515 break;
516
517 if( subDoc.docType )
518 {
520 continue;
521 }
522 else
523 {
525 continue;
526 }
527
528 EASYEDA::DOCUMENT dataStrDoc = subDoc.dataStr->get<EASYEDA::DOCUMENT>();
529
530 parser.ParseSchematic( aSchematic, aRootSheet, aFileName, dataStrDoc.shape );
531 }
532 }
533 }
534 catch( nlohmann::json::exception& e )
535 {
537 wxString::Format( _( "Error loading schematic '%s': %s" ), aFileName, e.what() ) );
538 }
539 catch( std::exception& e )
540 {
542 wxString::Format( _( "Error loading schematic '%s': %s" ), aFileName, e.what() ) );
543 }
544}
545
546
547SCH_SHEET* SCH_EASYEDA_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
548 SCH_SHEET* aAppendToMe,
549 const STRING_UTF8_MAP* aProperties )
550{
551 wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
552
553 SCH_SHEET* rootSheet = nullptr;
554
555 if( aAppendToMe )
556 {
557 wxCHECK_MSG( aSchematic->IsValid(), nullptr,
558 wxS( "Can't append to a schematic with no root!" ) );
559
560 rootSheet = &aSchematic->Root();
561 }
562 else
563 {
564 rootSheet = new SCH_SHEET( aSchematic );
565 rootSheet->SetFileName( aFileName );
566 aSchematic->SetRoot( rootSheet );
567 }
568
569 if( !rootSheet->GetScreen() )
570 {
571 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
572
573 screen->SetFileName( aFileName );
574 rootSheet->SetScreen( screen );
575 }
576
577 SYMBOL_LIB_TABLE* libTable = PROJECT_SCH::SchSymbolLibTable( &aSchematic->Prj() );
578
579 wxCHECK_MSG( libTable, nullptr, wxS( "Could not load symbol lib table." ) );
580 LoadSchematic( aSchematic, rootSheet, aFileName );
582
583 return rootSheet;
584}
const char * name
Definition: DXF_plotter.cpp:57
static double Convert(const wxString &aValue)
Define a library symbol object.
Definition: lib_symbol.h:99
static SYMBOL_LIB_TABLE * SchSymbolLibTable(PROJECT *aProject)
Accessor for project symbol library table.
Holds all the data relating to one schematic.
Definition: schematic.h:75
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
void SetRoot(SCH_SHEET *aRootSheet)
Initialize the schematic with a new root sheet.
Definition: schematic.cpp:113
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition: schematic.h:121
SCH_SHEET & Root() const
Definition: schematic.h:105
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:90
void ParseSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName, wxArrayString aShapes)
LIB_SYMBOL * ParseSymbol(const VECTOR2D &aOrigin, std::map< wxString, wxString > aParams, wxArrayString aShapes)
const wxString GetName() const override
Return a brief hard coded name for this SCH_PLUGIN.
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const STRING_UTF8_MAP *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this SCH_PLUGIN can read the specified symbol library file.
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const STRING_UTF8_MAP *aProperties=nullptr) override
Load information from some input file format that this SCH_PLUGIN implementation knows about,...
int GetModifyHash() const override
Return the modification hash from the library cache.
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_PLUGIN can read the specified schematic file.
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const STRING_UTF8_MAP *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_PLUGIN can read the specified schematic file.
Definition: sch_plugin.cpp:51
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:114
void UpdateAllScreenReferences() const
Update all the symbol references for this sheet path.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
void SetFileName(const wxString &aFilename)
Definition: sch_sheet.h:314
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:110
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:162
A name/value tuple with unique names and optional values.
#define _(s)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
wxString get_def(const std::map< wxString, wxString > &aMap, const char *aKey, const char *aDefval="")
Definition: map_helpers.h:64
static void LoadSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName)
static bool FindSchFileInStream(const wxString &aName, wxInputStream &aStream, nlohmann::json &aOut, EASYEDA::DOCUMENT &aDoc, EASYEDA::DOC_TYPE &aDocType)
LIB_SYMBOL * loadSymbol(const wxString &aLibraryPath, nlohmann::json aFileData, const wxString &aAliasName, const STRING_UTF8_MAP *aProperties)
std::optional< std::vector< DOCUMENT > > schematics
std::optional< std::map< wxString, wxString > > c_para
std::optional< nlohmann::json > dataStr
std::optional< DOC_TYPE > docType
std::optional< std::map< wxString, wxString > > c_para