KiCad PCB EDA Suite
Loading...
Searching...
No Matches
easyedapro_import_utils.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
26#include "easyedapro_parser.h"
27
29
30#include <ki_exception.h>
31#include <string_utils.h>
32#include <json_common.h>
34
35#include <wx/log.h>
36#include <wx/stream.h>
37#include <wx/zipstrm.h>
38#include <wx/wfstream.h>
39#include <wx/mstream.h>
40#include <wx/txtstrm.h>
41
42
43wxString EASYEDAPRO::ShortenLibName( wxString aProjectName )
44{
45 wxString shortenedName = aProjectName;
46 shortenedName.Replace( wxS( "ProProject_" ), wxS( "" ) );
47 shortenedName.Replace( wxS( "ProDocument_" ), wxS( "" ) );
48 shortenedName = shortenedName.substr( 0, 10 );
49
50 return LIB_ID::FixIllegalChars( shortenedName + wxS( "-easyedapro" ), true );
51}
52
53
54LIB_ID EASYEDAPRO::ToKiCadLibID( const wxString& aLibName, const wxString& aLibReference )
55{
56 wxString libName = LIB_ID::FixIllegalChars( aLibName, true );
57 wxString libReference = EscapeString( aLibReference, CTX_LIBID );
58
59 wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference;
60
61 LIB_ID libId;
62 libId.Parse( key, true );
63
64 return libId;
65}
66
67
68std::vector<IMPORT_PROJECT_DESC>
69EASYEDAPRO::ProjectToSelectorDialog( const nlohmann::json& aProject, bool aPcbOnly, bool aSchOnly )
70{
71 std::vector<IMPORT_PROJECT_DESC> result;
72
73 std::map<wxString, EASYEDAPRO::PRJ_SCHEMATIC> prjSchematics = aProject.at( "schematics" );
74 std::map<wxString, EASYEDAPRO::PRJ_BOARD> prjBoards = aProject.at( "boards" );
75 std::map<wxString, wxString> prjPcbNames = aProject.at( "pcbs" );
76
77 for( const auto& [prjName, board] : prjBoards )
78 {
80 desc.ComboName = desc.ComboId = prjName;
81 desc.PCBId = board.pcb;
82 desc.SchematicId = board.schematic;
83
84 auto pcbNameIt = prjPcbNames.find( desc.PCBId );
85 if( pcbNameIt != prjPcbNames.end() )
86 {
87 desc.PCBName = pcbNameIt->second;
88
89 if( desc.PCBName.empty() )
90 desc.PCBName = pcbNameIt->first;
91
92 prjPcbNames.erase( pcbNameIt );
93 }
94
95 auto schIt = prjSchematics.find( desc.SchematicId );
96 if( schIt != prjSchematics.end() )
97 {
98 desc.SchematicName = schIt->second.name;
99
100 if( desc.SchematicName.empty() )
101 desc.SchematicName = schIt->first;
102
103 prjSchematics.erase( schIt );
104 }
105
106 result.emplace_back( desc );
107 }
108
109 if( !aSchOnly )
110 {
111 for( const auto& [pcbId, pcbName] : prjPcbNames )
112 {
114 desc.PCBId = pcbId;
115 desc.PCBName = pcbName;
116
117 if( desc.PCBName.empty() )
118 desc.PCBName = pcbId;
119
120 result.emplace_back( desc );
121 }
122 }
123
124 if( !aPcbOnly )
125 {
126 for( const auto& [schId, schData] : prjSchematics )
127 {
129 desc.SchematicId = schId;
130 desc.SchematicName = schData.name;
131
132 if( desc.SchematicName.empty() )
133 desc.SchematicName = schId;
134
135 result.emplace_back( desc );
136 }
137 }
138
139 return result;
140}
141
142
143nlohmann::json EASYEDAPRO::FindJsonFile( const wxString& aZipFileName,
144 const std::set<wxString>& aFileNames )
145{
146 std::shared_ptr<wxZipEntry> entry;
147 wxFFileInputStream in( aZipFileName );
148 wxZipInputStream zip( in );
149
150 while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
151 {
152 wxString name = entry->GetName();
153
154 try
155 {
156 if( aFileNames.find( name ) != aFileNames.end() )
157 {
158 wxMemoryOutputStream memos;
159 memos << zip;
160 wxStreamBuffer* buf = memos.GetOutputStreamBuffer();
161
162 wxString str =
163 wxString::FromUTF8( (char*) buf->GetBufferStart(), buf->GetBufferSize() );
164
165 return nlohmann::json::parse( str );
166 }
167 }
168 catch( nlohmann::json::exception& e )
169 {
171 wxString::Format( _( "JSON error reading '%s': %s" ), name, e.what() ) );
172 }
173 catch( std::exception& e )
174 {
175 THROW_IO_ERROR( wxString::Format( _( "Error reading '%s': %s" ), name, e.what() ) );
176 }
177 }
178
179 return nlohmann::json{};
180}
181
182
183nlohmann::json EASYEDAPRO::ReadProjectOrDeviceFile( const wxString& aZipFileName )
184{
185 static const std::set<wxString> c_files = { wxS( "project.json" ), wxS( "device.json" ),
186 wxS( "footprint.json" ), wxS( "symbol.json" ) };
187
188 nlohmann::json j = FindJsonFile( aZipFileName, c_files );
189
190 if( !j.is_null() )
191 return j;
192
193 THROW_IO_ERROR( wxString::Format(
194 _( "'%s' does not appear to be a valid EasyEDA (JLCEDA) Pro "
195 "project or library file. Cannot find project.json or device.json." ),
196 aZipFileName ) );
197}
198
199
201 const wxString& aFileName,
202 std::function<bool( const wxString&, const wxString&, wxInputStream& )> aCallback )
203{
204 std::shared_ptr<wxZipEntry> entry;
205 wxFFileInputStream in( aFileName );
206 wxZipInputStream zip( in );
207
208 if( !zip.IsOk() )
209 {
210 THROW_IO_ERROR( wxString::Format( _( "Cannot read ZIP archive '%s'" ), aFileName ) );
211 }
212
213 while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL )
214 {
215 wxString name = entry->GetName();
216 wxString baseName = name.AfterLast( '\\' ).AfterLast( '/' ).BeforeFirst( '.' );
217
218 try
219 {
220 if( aCallback( name, baseName, zip ) )
221 break;
222 }
223 catch( nlohmann::json::exception& e )
224 {
226 wxString::Format( _( "JSON error reading '%s': %s" ), name, e.what() ) );
227 }
228 catch( std::exception& e )
229 {
230 THROW_IO_ERROR( wxString::Format( _( "Error reading '%s': %s" ), name, e.what() ) );
231 }
232 }
233}
234
235
236std::vector<nlohmann::json> EASYEDAPRO::ParseJsonLines( wxInputStream& aInput,
237 const wxString& aSource )
238{
239 wxTextInputStream txt( aInput, wxS( " " ), wxConvUTF8 );
240
241 int currentLine = 1;
242
243 std::vector<nlohmann::json> lines;
244 while( aInput.CanRead() )
245 {
246 try
247 {
248 wxString line = txt.ReadLine();
249
250 if( !line.IsEmpty() )
251 {
252 nlohmann::json js = nlohmann::json::parse( line );
253 lines.emplace_back( js );
254 }
255 else
256 {
257 lines.emplace_back( nlohmann::json() );
258 }
259 }
260 catch( nlohmann::json::exception& e )
261 {
262 wxLogWarning( wxString::Format( _( "Cannot parse JSON line %d in '%s': %s" ),
263 currentLine, aSource, e.what() ) );
264 }
265
266 currentLine++;
267 }
268
269 return lines;
270}
271
272
273std::vector<std::vector<nlohmann::json>>
274EASYEDAPRO::ParseJsonLinesWithSeparation( wxInputStream& aInput, const wxString& aSource )
275{
276 wxTextInputStream txt( aInput, wxS( " " ), wxConvUTF8 );
277
278 int currentLine = 1;
279
280 std::vector<std::vector<nlohmann::json>> lineBlocks;
281 lineBlocks.emplace_back();
282
283 while( aInput.CanRead() )
284 {
285 try
286 {
287 wxString line = txt.ReadLine();
288
289 if( !line.IsEmpty() )
290 {
291 nlohmann::json js = nlohmann::json::parse( line );
292 lineBlocks.back().emplace_back( js );
293 }
294 else
295 {
296 lineBlocks.emplace_back();
297 }
298 }
299 catch( nlohmann::json::exception& e )
300 {
301 wxLogWarning( wxString::Format( _( "Cannot parse JSON line %d in '%s': %s" ),
302 currentLine, aSource, e.what() ) );
303 }
304
305 currentLine++;
306 }
307
308 return lineBlocks;
309}
310
311
312std::map<wxString, wxString>
313EASYEDAPRO::AnyMapToStringMap( const std::map<wxString, nlohmann::json>& aInput )
314{
315 std::map<wxString, wxString> stringMap;
316
317 for( auto& [key, value] : aInput )
318 {
319 if( value.is_string() )
320 stringMap[key] = value.get<wxString>();
321 else if( value.is_number() )
322 stringMap[key] = wxString::FromCDouble( value.get<double>() );
323 }
324
325 return stringMap;
326}
const char * name
Definition: DXF_plotter.cpp:57
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:51
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:191
#define _(s)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
void IterateZipFiles(const wxString &aFileName, std::function< bool(const wxString &, const wxString &, wxInputStream &)> aCallback)
std::vector< nlohmann::json > ParseJsonLines(wxInputStream &aInput, const wxString &aSource)
std::vector< std::vector< nlohmann::json > > ParseJsonLinesWithSeparation(wxInputStream &aInput, const wxString &aSource)
Multiple document types (e.g.
nlohmann::json FindJsonFile(const wxString &aZipFileName, const std::set< wxString > &aFileNames)
std::vector< IMPORT_PROJECT_DESC > ProjectToSelectorDialog(const nlohmann::json &aProject, bool aPcbOnly=false, bool aSchOnly=false)
nlohmann::json ReadProjectOrDeviceFile(const wxString &aZipFileName)
wxString ShortenLibName(wxString aProjectName)
std::map< wxString, wxString > AnyMapToStringMap(const std::map< wxString, nlohmann::json > &aInput)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
Definition: string_utils.h:54
Describes how non-KiCad boards and schematics should be imported as KiCad projects.