KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_odbpp.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 * Author: SYSUEric <[email protected]>.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <algorithm>
22#include <set>
23
24#include "pcb_io_odbpp.h"
25#include "progress_reporter.h"
26#include "odb_util.h"
27#include "odb_attribute.h"
28
29#include "odb_defines.h"
30#include "odb_feature.h"
31#include "odb_entity.h"
32#include "wx/log.h"
33
34#include <footprint.h>
35#include <pad.h>
36#include <board.h>
40#include <netinfo.h>
41#include <ki_exception.h>
42#include <lset.h>
43#include <string_utils.h>
44
45
46namespace
47{
48bool HasIntentionalShorts( const BOARD& aBoard )
49{
50 for( const FOOTPRINT* footprint : aBoard.Footprints() )
51 {
52 if( !footprint->IsNetTie() )
53 continue;
54
55 std::map<wxString, int> groups = footprint->MapPadNumbersToNetTieGroups();
56 std::map<int, std::set<int>> groupNets;
57
58 for( const PAD* pad : footprint->Pads() )
59 {
60 auto group = groups.find( pad->GetNumber() );
61
62 if( group == groups.end() || group->second < 0 || pad->GetNetCode() <= 0 )
63 continue;
64
65 if( groupNets[group->second].insert( pad->GetNetCode() ).second && groupNets[group->second].size() > 1 )
66 {
67 return true;
68 }
69 }
70 }
71
72 return false;
73}
74} // namespace
75
76
81
82
87
88
90{
91 m_maxUid = 0;
92 m_netTieFeatures.clear();
93 m_netNames.clear();
94 m_netNames.emplace( 0, wxS( "$NONE$" ) );
95 std::map<int, wxString> bases;
96 std::map<int, wxString> anonymous = m_format.m_anonymizeNets ? FabAnonymousNetNames( *m_board )
97 : std::map<int, wxString>{};
98
99 for( const NETINFO_ITEM* net : m_board->GetNetInfo() )
100 {
101 if( net->GetNetCode() <= 0 )
102 continue;
103
104 wxString base = m_format.m_anonymizeNets ? anonymous.at( net->GetNetCode() )
105 : ODB::GenLegalNetName( net->GetNetname() );
106 bases.emplace( net->GetNetCode(), base );
107 }
108
109 std::vector<wxString> baseNames;
110 baseNames.reserve( bases.size() );
111
112 for( const auto& entry : bases )
113 baseNames.push_back( entry.second );
114
115 std::vector<wxString> names = ODB::UniqueNames( baseNames, { wxS( "$NONE$" ) }, wxString::npos );
116
117 size_t index = 0;
118
119 for( const auto& entry : bases )
120 m_netNames.emplace( entry.first, names[index++] );
121
130}
131
132
134{
135 for( const auto& entity : m_entities )
136 {
137 if( !entity->CreateDirectoryTree( writer ) )
138 throw std::runtime_error( "Failed in create directory tree process" );
139
140 try
141 {
142 entity->GenerateFiles( writer );
143 }
144 catch( const std::exception& e )
145 {
146 throw std::runtime_error( "Failed in generate files process.\n" + std::string( e.what() ) );
147 }
148
149 }
150 return true;
151}
152
153
154bool PCB_IO_ODBPP::ExportODB( const wxString& aFileName )
155{
156 try
157 {
158 std::shared_ptr<ODB_TREE_WRITER> writer =
159 std::make_shared<ODB_TREE_WRITER>( aFileName );
160 writer->SetRootPath( writer->GetCurrentPath() );
161
163 {
164 m_progressReporter->SetNumPhases( 3 );
165 m_progressReporter->BeginPhase( 0 );
166 m_progressReporter->Report( _( "Creating ODB++ Structure" ) );
167 }
168
169 CreateEntity();
170
172 {
173 m_progressReporter->SetCurrentProgress( 1.0 );
174 }
175
176 for( auto const& entity : m_entities )
177 {
178 entity->InitEntityData();
179 }
180
182 {
183 m_progressReporter->SetCurrentProgress( 1.0 );
184 m_progressReporter->AdvancePhase( _( "Exporting board to ODB++" ) );
185 }
186
187 if( !GenerateFiles( *writer ) )
188 return false;
189
190 return true;
191 }
192 catch( const std::exception& e )
193 {
194 Report( wxString::Format( "Exception in ODB++ ExportODB process: %s", e.what() ), RPT_SEVERITY_ERROR );
195 std::cerr << e.what() << std::endl;
196 return false;
197 }
198}
199
200
202{
203 std::vector<FOOTPRINT*> retval;
204 retval.reserve( m_loaded_footprints.size() );
205
206 for( const auto& fp : m_loaded_footprints )
207 {
208 retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
209 }
210
211 return retval;
212}
213
214
216{
217 if( !m_fabDrillModel )
218 m_fabDrillModel = std::make_unique<FAB_DRILL_MODEL>( *m_board );
219
220 return *m_fabDrillModel;
221}
222
223
225{
226 m_board = &aBoard;
227 m_entities.clear();
228 m_topeprint_subnets.clear();
229 m_plane_subnets.clear();
230 m_via_trace_subnets.clear();
231 m_fabDrillModel.reset();
232 m_fabStackup = std::make_unique<FAB_STACKUP>( aBoard );
233
234 // A second export on this plugin starts with the default format
236 m_format.m_variantName = aBoard.GetCurrentVariant();
237 m_format.m_variantNames = ODB::VARIANT_NAMES::Build( aBoard.GetVariantNames() );
238
239 if( aOptions.m_variant )
240 {
241 m_format.m_variantName = aOptions.m_variant->CmpNoCase( GetDefaultVariantName() ) == 0
242 ? wxString() : *aOptions.m_variant;
243 }
244
245 m_format.m_productModelName = aOptions.m_productModelName;
246 m_format.m_mpnField = aOptions.m_mpnField;
247 m_format.m_anonymizeNets = aOptions.m_anonymizeNets;
248 m_format.m_boardMetadata = aOptions.m_boardMetadata;
249 m_format.m_sections = aOptions.m_sections;
250
252 m_format.m_originOffset = aBoard.GetDesignSettings().GetAuxOrigin();
253 else if( aOptions.m_origin == JOB_EXPORT_PCB_ODB::ORIGIN::GRID )
254 m_format.m_originOffset = aBoard.GetDesignSettings().GetGridOrigin();
255
256 if( m_format.m_sections && m_format.m_sections->Contains( FAB::SECTION::COMPONENTS )
257 && !m_format.m_sections->Contains( FAB::SECTION::PACKAGES ) )
258 {
259 m_format.m_sections->Set( FAB::SECTION::PACKAGES );
260 Report( _( "ODB++ package section added because components reference packages." ),
262 }
263
264 for( const ODB_LAYER_OVERRIDE& override : aOptions.m_layerOverrides )
265 {
266 if( override.m_layer == UNDEFINED_LAYER )
267 {
268 Report( _( "Ignoring invalid ODB++ layer override." ), RPT_SEVERITY_WARNING );
269 continue;
270 }
271
272 m_format.m_layerOverrides.push_back( override );
273 }
274
275 if( aOptions.m_inch )
276 {
277 m_format.m_unitsStr = "INCH";
278 m_format.m_scale = ( 1.0 / 25.4 ) / PCB_IU_PER_MM;
279 m_format.m_symbolScale = ( 1.0 / 25.4 ) / PL_IU_PER_MM;
280 }
281
282 int precisionFloor = MinPrecision( aOptions.m_inch );
283 m_format.m_sigfig = std::clamp( aOptions.m_precision, precisionFloor, MaxPrecision() );
284
285 if( aOptions.m_precision < precisionFloor )
286 {
287 Report( wxString::Format( _( "ODB++ precision %d is below the minimum of %d for these units; "
288 "using %d." ), aOptions.m_precision, precisionFloor, m_format.m_sigfig ),
290 }
291 else if( aOptions.m_precision > MaxPrecision() )
292 {
293 Report( wxString::Format( _( "ODB++ precision %d is above the maximum of %d; using %d." ),
294 aOptions.m_precision, MaxPrecision(), m_format.m_sigfig ), RPT_SEVERITY_WARNING );
295 }
296
297 bool componentNets = m_format.Includes( FAB::SECTION::COMPONENTS );
298 bool logicalNets = m_format.Includes( FAB::SECTION::LOGICAL_NET );
299 bool shortNets = !componentNets && !logicalNets && m_format.Includes( FAB::SECTION::PHYSICAL_NET )
300 && HasIntentionalShorts( aBoard );
301 m_format.m_writeEdaNets = componentNets || logicalNets || shortNets;
302
303 if( shortNets )
304 Report( _( "ODB++ fabrication data includes EDA nets for intentional shorts." ), RPT_SEVERITY_WARNING );
305}
306
307
308void PCB_IO_ODBPP::Export( const wxString& aFileName, BOARD& aBoard, const ODB_EXPORT_OPTIONS& aOptions )
309{
310 ConfigureExport( aBoard, aOptions );
311
312 if( !ExportODB( aFileName ) )
313 THROW_IO_ERROR( _( "ODB++ export failed. See the messages above for the cause." ) );
314}
315
316
317void PCB_IO_ODBPP::SaveBoard( const wxString& aFileName, BOARD& aBoard, const std::map<std::string, UTF8>* aProperties )
318{
319 ODB_EXPORT_OPTIONS options;
320
321 if( aProperties )
322 {
323 if( auto it = aProperties->find( "units" ); it != aProperties->end() )
324 options.m_inch = it->second == "inch";
325
326 if( auto it = aProperties->find( "sigfig" ); it != aProperties->end() )
327 options.m_precision = std::stoi( it->second );
328 }
329
330 Export( aFileName, aBoard, options );
331}
int index
constexpr double PCB_IU_PER_MM
Pcbnew IU is 1 nanometer.
Definition base_units.h:68
constexpr double PL_IU_PER_MM
Internal units in micron (should be enough).
Definition base_units.h:69
const VECTOR2I & GetGridOrigin() const
const VECTOR2I & GetAuxOrigin() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const std::vector< wxString > & GetVariantNames() const
Definition board.h:532
const FOOTPRINTS & Footprints() const
Definition board.h:464
wxString GetCurrentVariant() const
Definition board.h:529
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
virtual void Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) const
Definition io_base.cpp:124
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:241
Handle the data for a net.
Definition netinfo.h:50
Definition pad.h:61
std::unique_ptr< FAB_STACKUP > m_fabStackup
BOARD * m_board
void ConfigureExport(BOARD &aBoard, const ODB_EXPORT_OPTIONS &aOptions)
ODB_FORMAT m_format
~PCB_IO_ODBPP() override
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
void ClearLoadedFootprints()
void Make(Args &&... args)
std::map< std::pair< PCB_LAYER_ID, ZONE * >, EDA_DATA::SUB_NET_PLANE * > m_plane_subnets
std::map< std::pair< const BOARD_ITEM *, PCB_LAYER_ID >, std::pair< wxString, size_t > > m_netTieFeatures
const FAB_DRILL_MODEL & GetFabDrillModel()
void CreateEntity()
bool ExportODB(const wxString &aFileName)
std::vector< std::shared_ptr< FOOTPRINT > > m_loaded_footprints
static int MinPrecision(bool aInches)
Lowest sigfig that still resolves a 0.1 mil grid: 3 decimal places in mm, 4 in inches.
void Export(const wxString &aFileName, BOARD &aBoard, const ODB_EXPORT_OPTIONS &aOptions)
Write the product model tree at aFileName Throw IO_ERROR when the write fails.
uint32_t m_maxUid
static int MaxPrecision()
Highest sigfig accepted by the exporter, matching the export dialog's spin control.
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
std::map< int, wxString > m_netNames
std::vector< std::shared_ptr< ODB_ENTITY_BASE > > m_entities
std::map< const PAD *, EDA_DATA::SUB_NET_TOEPRINT * > m_topeprint_subnets
bool GenerateFiles(ODB_TREE_WRITER &writer)
std::map< PCB_TRACK *, EDA_DATA::SUB_NET * > m_via_trace_subnets
std::unique_ptr< FAB_DRILL_MODEL > m_fabDrillModel
#define _(s)
std::map< int, wxString > FabAnonymousNetNames(const BOARD &aBoard)
Anonymous names NET_1 through NET_n keyed and numbered by net code.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ UNDEFINED_LAYER
Definition layer_ids.h:57
std::vector< wxString > UniqueNames(const std::vector< wxString > &aBases, const std::set< wxString > &aReserved, size_t aMaxLength)
Definition odb_util.cpp:252
wxString GenLegalNetName(const wxString &aStr)
Definition odb_util.cpp:161
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
wxString GetDefaultVariantName()
static VARIANT_NAMES Build(const std::vector< wxString > &aNames)
Definition odb_util.cpp:298
What a caller asks of one ODB++ export.
Definition odb_util.h:50
std::optional< wxString > m_variant
Unset exports the current variant of the board.
Definition odb_util.h:54
wxString m_mpnField
Definition odb_util.h:55
JOB_EXPORT_PCB_ODB::ORIGIN m_origin
Definition odb_util.h:57
std::optional< FAB::SECTION_SET > m_sections
Unset exports every section.
Definition odb_util.h:61
wxString m_productModelName
Definition odb_util.h:56
std::vector< ODB_LAYER_OVERRIDE > m_layerOverrides
Definition odb_util.h:62