KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_easyedapro_v3.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
28
31#include <pcb_io/pcb_io_mgr.h>
32
33#include <board.h>
34#include <font/fontconfig.h>
35#include <footprint.h>
37#include <ki_exception.h>
39#include <progress_reporter.h>
40#include <project_pcb.h>
41#include <reporter.h>
43
44#include <set>
45
46#include <wx/dir.h>
47#include <wx/filename.h>
48#include <wx/log.h>
49
50
52 PCB_IO( wxS( "EasyEDA (JLCEDA) Pro v3" ) )
53{
54}
55
56
58
59
61{
62 long long timestamp = GetLibraryTimestamp( aLibraryPath );
63
64 if( !m_cachedLibraryParser || m_cachedLibraryPath != aLibraryPath || m_cachedLibraryTimestamp != timestamp )
65 {
66 m_cachedLibraryParser = std::make_unique<EASYEDAPRO::V3_DOC_PARSER>( aLibraryPath );
67 m_cachedLibraryParser->LoadLibrary();
68 m_cachedLibraryPath = aLibraryPath;
69 m_cachedLibraryTimestamp = timestamp;
70 }
71
73}
74
75
76bool PCB_IO_EASYEDAPRO_V3::CanReadBoard( const wxString& aFileName ) const
77{
79}
80
81
82bool PCB_IO_EASYEDAPRO_V3::CanReadLibrary( const wxString& aFileName ) const
83{
84 if( !PCB_IO::CanReadLibrary( aFileName ) )
85 return false;
86
87 return EASYEDAPRO::V3_DOC_PARSER::IsV3Library( aFileName, wxS( "FOOTPRINT" ) );
88}
89
90
91long long PCB_IO_EASYEDAPRO_V3::GetLibraryTimestamp( const wxString& aLibraryPath ) const
92{
93 wxFileName fileName( aLibraryPath );
94
95 if( !fileName.FileExists() )
96 return 0;
97
98 wxDateTime modified = fileName.GetModificationTime();
99
100 if( !modified.IsValid() )
101 return 0;
102
103 return modified.GetTicks();
104}
105
106
107void PCB_IO_EASYEDAPRO_V3::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
108 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
109{
110 const EASYEDAPRO::V3_DOC_PARSER& v3 = getCachedLibraryParser( aLibraryPath );
111
112 std::map<wxString, wxString> footprintMap =
113 EASYEDAPRO::BuildV3LibraryItemMap( v3, "footprints", wxS( "FOOTPRINT" ) );
114
115 for( const auto& [name, uuid] : footprintMap )
116 aFootprintNames.Add( name );
117}
118
119
120FOOTPRINT* PCB_IO_EASYEDAPRO_V3::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
121 bool aKeepUUID, const std::map<std::string, UTF8>* aProperties )
122{
123 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
124
125 const EASYEDAPRO::V3_DOC_PARSER& v3 = getCachedLibraryParser( aLibraryPath );
126
127 std::map<wxString, wxString> footprintMap =
128 EASYEDAPRO::BuildV3LibraryItemMap( v3, "footprints", wxS( "FOOTPRINT" ) );
129
130 auto fpIt = footprintMap.find( aFootprintName );
131
132 if( fpIt == footprintMap.end() )
133 return nullptr;
134
135 const EASYEDAPRO::V3_DOC_RAW* rawDoc = v3.FindRawDoc( wxS( "FOOTPRINT" ), fpIt->second );
136
137 if( !rawDoc )
138 return nullptr;
139
140 const nlohmann::json& index = v3.GetLibraryIndex();
141
142 PCB_IO_EASYEDAPRO_V3_PARSER parser( nullptr, nullptr );
143 FOOTPRINT* footprint = nullptr;
144
145 try
146 {
147 footprint = parser.ParseFootprint( EASYEDAPRO::BuildV3BlobMap( v3 ), *rawDoc );
148 }
149 catch( nlohmann::json::exception& e )
150 {
151 THROW_IO_ERROR( wxString::Format( _( "Cannot load footprint '%s' from '%s': %s" ), aFootprintName, aLibraryPath,
152 e.what() ) );
153 }
154
155 if( !footprint )
156 {
157 THROW_IO_ERROR( wxString::Format( _( "Cannot load footprint '%s' from '%s'" ), aFootprintName, aLibraryPath ) );
158 }
159
160 // Library footprints are keyed by footprint geometry; pick any matching device's 3D model.
161 if( index.contains( "devices" ) && index.at( "devices" ).is_object() )
162 {
163 for( const auto& [devUuid, deviceJson] : index.at( "devices" ).items() )
164 {
165 if( !deviceJson.is_object() || !deviceJson.contains( "attributes" )
166 || !deviceJson.at( "attributes" ).is_object() )
167 {
168 continue;
169 }
170
171 const nlohmann::json& attrs = deviceJson.at( "attributes" );
172
173 if( !attrs.contains( "Footprint" )
174 || EASYEDAPRO::V3JsonToString( attrs.at( "Footprint" ) ) != fpIt->second )
175 {
176 continue;
177 }
178
179 wxString modelUuid;
180 wxString modelTitle;
181 wxString modelTransform;
182
183 if( attrs.contains( "3D Model" ) )
184 modelUuid = EASYEDAPRO::V3JsonToString( attrs.at( "3D Model" ) );
185
186 if( attrs.contains( "3D Model Title" ) )
187 modelTitle = EASYEDAPRO::V3JsonToString( attrs.at( "3D Model Title" ) ).Trim();
188 else
189 modelTitle = modelUuid;
190
191 if( attrs.contains( "3D Model Transform" ) )
192 modelTransform = EASYEDAPRO::V3JsonToString( attrs.at( "3D Model Transform" ) );
193
194 PCB_IO_EASYEDAPRO_PARSER modelParser( nullptr, nullptr );
195 modelParser.FillFootprintModelInfo( footprint, modelUuid, modelTitle, modelTransform );
196
197 break;
198 }
199 }
200
201 footprint->SetFPID( EASYEDAPRO::ToKiCadLibID( wxEmptyString, aFootprintName ) );
202 footprint->Reference().SetVisible( true );
203 footprint->Value().SetText( aFootprintName );
204 footprint->Value().SetVisible( true );
205 footprint->AutoPositionFields();
206
207 return footprint;
208}
209
210
211BOARD* PCB_IO_EASYEDAPRO_V3::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
212 const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
213{
214 m_props = aProperties;
215
216 m_board = aAppendToMe ? aAppendToMe : new BOARD();
217
218 if( !aAppendToMe )
219 m_board->SetFileName( aFileName );
220
222
224 {
225 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
226
227 if( !m_progressReporter->KeepRefreshing() )
228 THROW_IO_ERROR( _( "File import canceled by user." ) );
229 }
230
231 EASYEDAPRO::V3_DOC_PARSER adapter( aFileName );
232 adapter.Load();
233
234 nlohmann::json project = EASYEDAPRO::BuildV3ProjectIndexFromRawDocs( adapter );
235
236 wxString pcbToLoad;
237
238 if( m_props && m_props->contains( "pcb_id" ) )
239 {
240 pcbToLoad = wxString::FromUTF8( m_props->at( "pcb_id" ) );
241 }
242 else
243 {
244 std::map<wxString, nlohmann::json> prjPcbs = project.at( "pcbs" );
245
246 if( prjPcbs.size() == 1 )
247 {
248 pcbToLoad = prjPcbs.begin()->first;
249 }
250 else if( m_choose_project_handler )
251 {
252 std::vector<IMPORT_PROJECT_DESC> chosen =
254
255 if( !chosen.empty() )
256 pcbToLoad = chosen[0].PCBId;
257 }
258 else if( !prjPcbs.empty() )
259 {
260 pcbToLoad = prjPcbs.begin()->first;
261 }
262 }
263
264 if( pcbToLoad.empty() )
265 return nullptr;
266
267 PCB_IO_EASYEDAPRO_V3_PARSER parser( nullptr, nullptr );
268
269 std::map<wxString, EASYEDAPRO::BLOB> blobs = EASYEDAPRO::BuildV3BlobMap( adapter );
270
271 wxFileName sourceName( aFileName );
272 wxString fpLibName = EASYEDAPRO::ShortenLibName( sourceName.GetName() );
273 wxString prettyDirName = fpLibName + wxS( "." ) + wxString::FromUTF8( FILEEXT::KiCadFootprintLibPathExtension );
274
275 wxFileName libDirName;
276 libDirName.AssignDir( sourceName.GetPath() );
277 libDirName.AppendDir( prettyDirName );
278 wxString libPath = libDirName.GetPath();
279
280 // PCB components reference Footprint (geometry) and Device (BOM / 3D attrs) separately.
281 // Project .pretty entries are named by footprint package title (e.g. C0402).
282 std::map<wxString, std::unique_ptr<FOOTPRINT>> footprints;
283 std::set<wxString> usedLibNames;
284
285 for( const auto& [uuid, rawDoc] : adapter.GetRawDocs( wxS( "FOOTPRINT" ) ) )
286 {
287 std::unique_ptr<FOOTPRINT> footprint( parser.ParseFootprint( blobs, rawDoc ) );
288
289 if( !footprint )
290 continue;
291
292 std::string uuidKey = std::string( uuid.ToUTF8() );
293 const nlohmann::json& fpMetas = project.at( "footprints" );
294 wxString fpTitle = uuid;
295
296 if( fpMetas.contains( uuidKey ) )
297 fpTitle = EASYEDAPRO::GetV3LibraryItemTitle( fpMetas.at( uuidKey ), uuid );
298
299 wxString itemName = EASYEDAPRO::MakeUniqueLibName( usedLibNames, fpTitle, uuid );
300 footprint->SetFPID( EASYEDAPRO::ToKiCadLibID( fpLibName, itemName ) );
301 footprints.emplace( uuid, std::move( footprint ) );
302 }
303
304 const EASYEDAPRO::V3_DOC_RAW* pcbRawDoc = adapter.FindRawDoc( wxS( "PCB" ), pcbToLoad );
305
306 if( !pcbRawDoc )
307 {
308 THROW_IO_ERROR( wxString::Format( _( "PCB document '%s' not found in '%s'" ), pcbToLoad, aFileName ) );
309 }
310
311 std::multimap<wxString, EASYEDAPRO::POURED> poured; // Empty - v3 parser extracts from raw doc
312
313 parser.ParseBoard( m_board, project, footprints, blobs, poured, *pcbRawDoc, fpLibName );
314
315 // Create a project-local .pretty library (one entry per footprint geometry).
316 if( !footprints.empty() )
317 {
319
320 if( !wxDir::Exists( libPath ) )
321 pcbPlugin->CreateLibrary( libPath );
322
323 PROJECT* proj = aProject ? aProject : m_board->GetProject();
324
325 if( proj )
326 {
328 LIBRARY_TABLE* table = fpAdapter->ProjectTable().value_or( nullptr );
329
330 if( table && !table->HasRow( fpLibName ) )
331 {
332 wxString libTableUri = wxS( "${KIPRJMOD}/" ) + prettyDirName;
333
334 LIBRARY_TABLE_ROW& row = table->InsertRow();
335 row.SetNickname( fpLibName );
336 row.SetURI( libTableUri );
337 row.SetType( "KiCad" );
338
339 table->Save();
340 fpAdapter->LoadOne( fpLibName );
341 }
342 }
343
344 for( auto& [uuid, footprint] : footprints )
345 {
346 if( footprint )
347 pcbPlugin->FootprintSave( libPath, footprint.get() );
348 }
349 }
350
351 if( adapter.GetSkippedCount() > 0 )
352 {
353 wxLogWarning( wxString::Format( _( "EasyEDA (JLCEDA) Pro v3 import skipped %d unsupported object(s)." ),
354 adapter.GetSkippedCount() ) );
355 }
356
357 return m_board;
358}
int index
const char * name
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
Parses EasyEDA Pro v3 .epro2 archives.
void Load()
Load all v3 docs from the archive.
const V3_DOC_RAW * FindRawDoc(const wxString &aDocType, const wxString &aUuid) const
const std::map< wxString, V3_DOC_RAW > & GetRawDocs(const wxString &aDocType) const
static bool IsV3Library(const wxString &aFileName, const wxString &aRequiredDocType=wxEmptyString)
Return true if aFileName looks like an EasyEDA Pro v3 library archive.
static bool IsV3Archive(const wxString &aFileName)
Return true if aFileName looks like an EasyEDA Pro v3 archive.
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:265
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:368
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:445
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:880
PCB_FIELD & Reference()
Definition footprint.h:881
void AutoPositionFields()
Position Reference and Value fields at the top and bottom of footprint's bounding box.
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:240
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetURI(const wxString &aUri)
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:306
void FillFootprintModelInfo(FOOTPRINT *footprint, const wxString &modelUuid, const wxString &modelTitle, const wxString &modelTransform) const
void ParseBoard(BOARD *aBoard, const nlohmann::json &aProject, std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap, const std::multimap< wxString, EASYEDAPRO::POURED > &aPouredMap, const EASYEDAPRO::V3_DOC_RAW &aDoc, const wxString &aFpLibName)
FOOTPRINT * ParseFootprint(const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap, const EASYEDAPRO::V3_DOC_RAW &aDoc)
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
std::unique_ptr< EASYEDAPRO::V3_DOC_PARSER > m_cachedLibraryParser
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
~PCB_IO_EASYEDAPRO_V3() override
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
const EASYEDAPRO::V3_DOC_PARSER & getCachedLibraryParser(const wxString &aLibraryPath) const
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:54
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:349
PCB_IO(const wxString &aName)
Definition pcb_io.h:342
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:352
CHOOSE_PROJECT_HANDLER m_choose_project_handler
Callback to choose projects to import.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:63
#define _(s)
static const std::string KiCadFootprintLibPathExtension
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
nlohmann::json BuildV3ProjectIndexFromRawDocs(const V3_DOC_PARSER &aParser, bool aIncludeLibraryMetadata=true)
Build a minimal legacy-style project index from parsed v3 raw documents.
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
wxString MakeUniqueLibName(std::set< wxString > &aUsedNames, const wxString &aName, const wxString &aFallback)
Allocate a unique library item name, recording it in aUsedNames.
wxString GetV3LibraryItemTitle(const nlohmann::json &aMetadata, const wxString &aUuid)
std::vector< IMPORT_PROJECT_DESC > ProjectToSelectorDialog(const nlohmann::json &aProject, bool aPcbOnly=false, bool aSchOnly=false)
wxString ShortenLibName(wxString aProjectName)
wxString V3JsonToString(const nlohmann::json &aValue, const wxString &aDefault)
std::map< wxString, wxString > BuildV3LibraryItemMap(const V3_DOC_PARSER &aParser, const char *aIndexKey, const wxString &aDocType)
std::map< wxString, BLOB > BuildV3BlobMap(const V3_DOC_PARSER &aParser)
Raw parsed document from an EasyEDA Pro v3 .epru stream.
std::vector< std::vector< std::string > > table
VECTOR2I v3(-2, 1)
Definition of file extensions used in Kicad.