KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gerber_jobfile_writer.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) 2018 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright The 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
29
30#include <fstream>
31#include <iomanip>
32#include <vector>
33
34#include <build_version.h>
35#include <common.h>
36#include <locale_io.h>
37#include <pcb_edit_frame.h>
38#include <plotters/plotter.h>
39
40#include <board.h>
43#include <footprint.h>
44#include <pad.h>
45#include <pcb_track.h>
46#include <zone.h>
47
49#include <gbr_metadata.h>
51#include <pcbplot.h>
52#include <reporter.h>
54
55
57{
58 m_pcb = aPcb;
59 m_reporter = aReporter;
60 m_conversionUnits = 1.0 / pcbIUScale.IU_PER_MM; // Gerber units = mm
61}
62
63std::string GERBER_JOBFILE_WRITER::formatStringFromUTF32( const wxString& aText )
64{
65 std::string fmt_text; // the text after UTF32 to UTF8 conversion
66 fmt_text = aText.utf8_string();
67
68 return fmt_text;
69}
70
71
73{
74 int flag = SIDE_NONE;
75
76 for( PCB_LAYER_ID layer : m_params.m_LayerId )
77 {
78 if( layer == B_SilkS )
80
81 if( layer == F_SilkS )
82 flag |= SIDE_TOP;
83 }
84
85 return (enum ONSIDE) flag;
86}
87
88
90{
91 int flag = SIDE_NONE;
92
93 for( PCB_LAYER_ID layer : m_params.m_LayerId )
94 {
95 if( layer == B_Mask )
97
98 if( layer == F_Mask )
99 flag |= SIDE_TOP;
100 }
101
102 return (enum ONSIDE) flag;
103}
104
106{
107 // return the key associated to sides used for some layers
108 // "No, TopOnly, BotOnly or Both"
109 const char* value = nullptr;
110
111 switch( aValue )
112 {
113 case SIDE_NONE: value = "No"; break;
114 case SIDE_TOP: value = "TopOnly"; break;
115 case SIDE_BOTTOM: value = "BotOnly"; break;
116 case SIDE_BOTH: value = "Both"; break;
117 }
118
119 return value;
120}
121
122
123bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
124{
125 bool success;
126 wxString msg;
127
128 success = WriteJSONJobFile( aFullFilename );
129
130 if( !success )
131 {
132 if( m_reporter )
133 {
134 msg.Printf( _( "Failed to create file '%s'." ), aFullFilename );
135 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
136 }
137 }
138 else if( m_reporter )
139 {
140 msg.Printf( _( "Created Gerber job file '%s'." ), aFullFilename );
141 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
142 }
143
144 return success;
145}
146
147
149{
150 m_json["Header"] = {
151 {
152 "GenerationSoftware",
153 {
154 { "Vendor", "KiCad" },
155 { "Application", "Pcbnew" },
156 { "Version", GetBuildVersion() }
157 }
158 },
159 {
160 // The attribute value must conform to the full version of the ISO 8601
161 // date and time format, including time and time zone.
163 }
164 };
165}
166
167
168bool GERBER_JOBFILE_WRITER::WriteJSONJobFile( const wxString& aFullFilename )
169{
170 // Note: in Gerber job file, dimensions are in mm, and are floating numbers
171 std::ofstream file( aFullFilename.ToUTF8() );
172
173 m_json = nlohmann::ordered_json( {} );
174
175 // output the job file header
177
178 // Add the General Specs
180
181 // Job file support a few design rules:
183
184 // output the gerber file list:
186
187 // output the board stackup:
189
190 file << std::setw( 2 ) << m_json << std::endl;
191
192 return true;
193}
194
195
196double GERBER_JOBFILE_WRITER::mapValue( double aUiValue )
197{
198 // A helper function to convert aUiValue in Json units (mm) and to have
199 // 4 digits in Json in mantissa when using %g to print it
200 // i.e. displays values truncated in 0.1 microns.
201 // This is enough for a Json file
202 char buffer[128];
203 std::snprintf( buffer, sizeof( buffer ), "%.4f", aUiValue * m_conversionUnits );
204
205 long double output;
206 sscanf( buffer, "%Lg", &output );
207
208 return output;
209
210}
211
212
214{
215 m_json["GeneralSpecs"] = nlohmann::ordered_json( {} );
216 m_json["GeneralSpecs"]["ProjectId"] = nlohmann::ordered_json( {} );
217
218 // Creates the ProjectId. Format is (from Gerber file format doc):
219 // ProjectId,<project id>,<project GUID>,<revision id>*%
220 // <project id> is the name of the project, restricted to basic ASCII symbols only,
221 // and comma not accepted
222 // All illegal chars will be replaced by underscore
223 // Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
224 //
225 // <project GUID> is a string which is an unique id of a project.
226 // However Kicad does not handle such a project GUID, so it is built from the board name
227 wxFileName fn = m_pcb->GetFileName();
228 wxString msg = fn.GetFullName();
229
230 // Build a <project GUID>, from the board name
231 wxString guid = GbrMakeProjectGUIDfromString( msg );
232
233 // build the <project id> string: this is the board short filename (without ext)
234 // and in UTF8 format.
235 msg = fn.GetName();
236
237 // build the <rev> string. All non ASCII chars are in UTF8 form
238 wxString rev = ExpandTextVars( m_pcb->GetTitleBlock().GetRevision(), m_pcb->GetProject() );
239
240 if( rev.IsEmpty() )
241 rev = wxT( "rev?" );
242
243 m_json["GeneralSpecs"]["ProjectId"]["Name"] = msg.utf8_string().c_str();
244 m_json["GeneralSpecs"]["ProjectId"]["GUID"] = guid;
245 m_json["GeneralSpecs"]["ProjectId"]["Revision"] = rev.utf8_string().c_str();
246
247 // output the board size in mm:
248 BOX2I brect = m_pcb->GetBoardEdgesBoundingBox();
249
250 m_json["GeneralSpecs"]["Size"]["X"] = mapValue( brect.GetWidth() );
251 m_json["GeneralSpecs"]["Size"]["Y"] = mapValue( brect.GetHeight() );
252
253
254 // Add some data to the JSON header, GeneralSpecs:
255 // number of copper layers
256 m_json["GeneralSpecs"]["LayerNumber"] = m_pcb->GetCopperLayerCount();
257
258 // Board thickness
259 m_json["GeneralSpecs"]["BoardThickness"] =
260 mapValue( m_pcb->GetDesignSettings().GetBoardThickness() );
261
262 // Copper finish
263 const BOARD_STACKUP brd_stackup = m_pcb->GetDesignSettings().GetStackupDescriptor();
264
265 if( !brd_stackup.m_FinishType.IsEmpty() )
266 m_json["GeneralSpecs"]["Finish"] = brd_stackup.m_FinishType;
267
268 if( brd_stackup.m_HasDielectricConstrains )
269 m_json["GeneralSpecs"]["ImpedanceControlled"] = true;
270
271 #if 0 // Old way to set property
272 if( brd_stackup.m_CastellatedPads )
273 m_json["GeneralSpecs"]["Castellated"] = true;
274 #endif
275 if( m_pcb->GetPadWithCastellatedAttrCount() )
276 m_json["GeneralSpecs"]["Castellated"] = true;
277
278 if( m_pcb->GetPadWithPressFitAttrCount() )
279 m_json["GeneralSpecs"]["Press-fit"] = true;
280
281 if( brd_stackup.m_EdgePlating )
282 m_json["GeneralSpecs"]["EdgePlating"] = true;
283
284 if( brd_stackup.m_EdgeConnectorConstraints )
285 {
286 m_json["GeneralSpecs"]["EdgeConnector"] = true;
287
288 m_json["GeneralSpecs"]["EdgeConnectorBevelled"] =
290 }
291
292#if 0 // Not yet in use
293 /* The board type according to IPC-2221. There are six primary board types:
294 - Type 1 - Single-sided
295 - Type 2 - Double-sided
296 - Type 3 - Multilayer, TH components only
297 - Type 4 - Multilayer, with TH, blind and/or buried vias.
298 - Type 5 - Multilayer metal-core board, TH components only
299 - Type 6 - Multilayer metal-core
300 */
301 m_json["GeneralSpecs"]["IPC-2221-Type"] = 4;
302
303 /* Via protection: key words:
304 Ia Tented - Single-sided
305 Ib Tented - Double-sided
306 IIa Tented and Covered - Single-sided
307 IIb Tented and Covered - Double-sided
308 IIIa Plugged - Single-sided
309 IIIb Plugged - Double-sided
310 IVa Plugged and Covered - Single-sided
311 IVb Plugged and Covered - Double-sided
312 V Filled (fully plugged)
313 VI Filled and Covered
314 VIII Filled and Capped
315 None...No protection
316 */
317 m_json["GeneralSpecs"]["ViaProtection"] = "Ib";
318#endif
319}
320
321
323{
324 // Add the Files Attributes section in JSON format to m_JSONbuffer
325 m_json["FilesAttributes"] = nlohmann::ordered_json::array();
326
327 for( unsigned ii = 0; ii < m_params.m_GerberFileList.GetCount(); ii++ )
328 {
329 wxString& name = m_params.m_GerberFileList[ii];
330 PCB_LAYER_ID layer = m_params.m_LayerId[ii];
331 wxString gbr_layer_id;
332 bool skip_file = false; // true to skip files which should not be in job file
333 const char* polarity = "Positive";
334
335 nlohmann::ordered_json file_json;
336
337 if( IsCopperLayer( layer ) )
338 {
339 gbr_layer_id = wxT( "Copper,L" );
340
341 if( layer == B_Cu )
342 gbr_layer_id << m_pcb->GetCopperLayerCount();
343 else if( layer == F_Cu )
344 gbr_layer_id << 1;
345 else // Copper layers are numbered B_Cu + n*2 for inner layer n (n = 1 ... val max)
346 // and gbr_layer_id = 2 ... val max
347 gbr_layer_id << (layer-B_Cu) / 2 + 1;
348
349 gbr_layer_id << wxT( "," );
350
351 if( layer == B_Cu )
352 gbr_layer_id << wxT( "Bot" );
353 else if( layer == F_Cu )
354 gbr_layer_id << wxT( "Top" );
355 else
356 gbr_layer_id << wxT( "Inr" );
357 }
358
359 else
360 {
361 switch( layer )
362 {
363 case B_Adhes:
364 gbr_layer_id = wxT( "Glue,Bot" );
365 break;
366 case F_Adhes:
367 gbr_layer_id = wxT( "Glue,Top" );
368 break;
369
370 case B_Paste:
371 gbr_layer_id = wxT( "SolderPaste,Bot" );
372 break;
373 case F_Paste:
374 gbr_layer_id = wxT( "SolderPaste,Top" );
375 break;
376
377 case B_SilkS:
378 gbr_layer_id = wxT( "Legend,Bot" );
379 break;
380 case F_SilkS:
381 gbr_layer_id = wxT( "Legend,Top" );
382 break;
383
384 case B_Mask:
385 gbr_layer_id = wxT( "SolderMask,Bot" );
386 polarity = "Negative";
387 break;
388 case F_Mask:
389 gbr_layer_id = wxT( "SolderMask,Top" );
390 polarity = "Negative";
391 break;
392
393 case Edge_Cuts:
394 gbr_layer_id = wxT( "Profile" );
395 break;
396
397 case B_Fab:
398 gbr_layer_id = wxT( "AssemblyDrawing,Bot" );
399 break;
400 case F_Fab:
401 gbr_layer_id = wxT( "AssemblyDrawing,Top" );
402 break;
403
404 case Margin:
405 case B_CrtYd:
406 case F_CrtYd:
407 skip_file = true;
408 break;
409
410 case Dwgs_User:
411 case Cmts_User:
412 case Eco1_User:
413 case Eco2_User:
414 case User_1:
415 case User_2:
416 case User_3:
417 case User_4:
418 case User_5:
419 case User_6:
420 case User_7:
421 case User_8:
422 case User_9:
423 gbr_layer_id = wxT( "Other,User" );
424 break;
425
426 default:
427 skip_file = true;
428
429 if( m_reporter )
430 m_reporter->Report( wxT( "Unexpected layer id in job file" ), RPT_SEVERITY_ERROR );
431
432 break;
433 }
434 }
435
436 if( !skip_file )
437 {
438 // name can contain non ASCII7 chars.
439 // Ensure the name is JSON compatible.
440 std::string strname = formatStringFromUTF32( name );
441
442 file_json["Path"] = strname.c_str();
443 file_json["FileFunction"] = gbr_layer_id;
444 file_json["FilePolarity"] = polarity;
445
446 m_json["FilesAttributes"] += file_json;
447 }
448 }
449}
450
451
453{
454 // Add the Design Rules section in JSON format to m_JSONbuffer
455 // Job file support a few design rules:
456 std::shared_ptr<NET_SETTINGS>& netSettings = m_pcb->GetDesignSettings().m_NetSettings;
457
458 int minclearanceOuter = netSettings->GetDefaultNetclass()->GetClearance();
459 bool hasInnerLayers = m_pcb->GetCopperLayerCount() > 2;
460
461 // Search a smaller clearance in other net classes, if any.
462 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
463 minclearanceOuter = std::min( minclearanceOuter, netclass->GetClearance() );
464
465 // job file knows different clearance types.
466 // Kicad knows only one clearance for pads and tracks
467 int minclearance_track2track = minclearanceOuter;
468
469 // However, pads can have a specific clearance defined for a pad or a footprint,
470 // and min clearance can be dependent on layers.
471 // Search for a minimal pad clearance:
472 int minPadClearanceOuter = netSettings->GetDefaultNetclass()->GetClearance();
473 int minPadClearanceInner = netSettings->GetDefaultNetclass()->GetClearance();
474
475 for( FOOTPRINT* footprint : m_pcb->Footprints() )
476 {
477 for( PAD* pad : footprint->Pads() )
478 {
479 for( PCB_LAYER_ID layer : pad->GetLayerSet() )
480 {
481 int padClearance = pad->GetOwnClearance( layer );
482
483 if( layer == B_Cu || layer == F_Cu )
484 minPadClearanceOuter = std::min( minPadClearanceOuter, padClearance );
485 else
486 minPadClearanceInner = std::min( minPadClearanceInner, padClearance );
487 }
488 }
489 }
490
491 m_json["DesignRules"] = { {
492 { "Layers", "Outer" },
493 { "PadToPad", mapValue( minPadClearanceOuter ) },
494 { "PadToTrack", mapValue( minPadClearanceOuter ) },
495 { "TrackToTrack", mapValue( minclearance_track2track ) }
496 } };
497
498 // Until this is changed in Kicad, use the same value for internal tracks
499 int minclearanceInner = minclearanceOuter;
500
501 // Output the minimal track width
502 int mintrackWidthOuter = INT_MAX;
503 int mintrackWidthInner = INT_MAX;
504
505 for( PCB_TRACK* track : m_pcb->Tracks() )
506 {
507 if( track->Type() == PCB_VIA_T )
508 continue;
509
510 if( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
511 mintrackWidthOuter = std::min( mintrackWidthOuter, track->GetWidth() );
512 else
513 mintrackWidthInner = std::min( mintrackWidthInner, track->GetWidth() );
514 }
515
516 if( mintrackWidthOuter != INT_MAX )
517 m_json["DesignRules"][0]["MinLineWidth"] = mapValue( mintrackWidthOuter );
518
519 // Output the minimal zone to xx clearance
520 // Note: zones can have a zone clearance set to 0
521 // if happens, the actual zone clearance is the clearance of its class
522 minclearanceOuter = INT_MAX;
523 minclearanceInner = INT_MAX;
524
525 for( ZONE* zone : m_pcb->Zones() )
526 {
527 if( zone->GetIsRuleArea() || !zone->IsOnCopperLayer() )
528 continue;
529
530 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
531 {
532 int zclerance = zone->GetOwnClearance( layer );
533
534 if( layer == B_Cu || layer == F_Cu )
535 minclearanceOuter = std::min( minclearanceOuter, zclerance );
536 else
537 minclearanceInner = std::min( minclearanceInner, zclerance );
538 }
539 }
540
541 if( minclearanceOuter != INT_MAX )
542 m_json["DesignRules"][0]["TrackToRegion"] = mapValue( minclearanceOuter );
543
544 if( minclearanceOuter != INT_MAX )
545 m_json["DesignRules"][0]["RegionToRegion"] = mapValue( minclearanceOuter );
546
547 if( hasInnerLayers )
548 {
549 m_json["DesignRules"] += nlohmann::ordered_json( {
550 { "Layers", "Inner" },
551 { "PadToPad", mapValue( minPadClearanceInner ) },
552 { "PadToTrack", mapValue( minPadClearanceInner ) },
553 { "TrackToTrack", mapValue( minclearance_track2track ) }
554 } );
555
556 if( mintrackWidthInner != INT_MAX )
557 m_json["DesignRules"][1]["MinLineWidth"] = mapValue( mintrackWidthInner );
558
559 if( minclearanceInner != INT_MAX )
560 m_json["DesignRules"][1]["TrackToRegion"] = mapValue( minclearanceInner );
561
562 if( minclearanceInner != INT_MAX )
563 m_json["DesignRules"][1]["RegionToRegion"] = mapValue( minclearanceInner );
564 }
565}
566
567
569{
570 // Add the Material Stackup section in JSON format to m_JSONbuffer
571 m_json["MaterialStackup"] = nlohmann::ordered_json::array();
572
573 // Build the candidates list:
574 LSET maskLayer;
575 BOARD_STACKUP brd_stackup = m_pcb->GetDesignSettings().GetStackupDescriptor();
576
577 // Ensure brd_stackup is up to date (i.e. no change made by SynchronizeWithBoard() )
578 bool uptodate = not brd_stackup.SynchronizeWithBoard( &m_pcb->GetDesignSettings() );
579
580 if( m_reporter && !uptodate && m_pcb->GetDesignSettings().m_HasStackup )
581 m_reporter->Report( _( "Board stackup settings not up to date." ), RPT_SEVERITY_ERROR );
582
583 PCB_LAYER_ID last_copper_layer = F_Cu;
584
585 // Generate the list (top to bottom):
586 for( int ii = 0; ii < brd_stackup.GetCount(); ++ii )
587 {
588 BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii );
589
590 int sub_layer_count =
591 item->GetType() == BS_ITEM_TYPE_DIELECTRIC ? item->GetSublayersCount() : 1;
592
593 for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ )
594 {
595 // layer thickness is always in mm
596 double thickness = mapValue( item->GetThickness( sub_idx ) );
597 wxString layer_type;
598 std::string layer_name; // for comment
599
600 nlohmann::ordered_json layer_json;
601
602 switch( item->GetType() )
603 {
605 layer_type = wxT( "Copper" );
606 layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) );
607 last_copper_layer = item->GetBrdLayerId();
608 break;
609
611 layer_type = wxT( "Legend" );
612 layer_name = formatStringFromUTF32( item->GetTypeName() );
613 break;
614
616 layer_type = wxT( "SolderMask" );
617 layer_name = formatStringFromUTF32( item->GetTypeName() );
618 break;
619
621 layer_type = wxT( "SolderPaste" );
622 layer_name = formatStringFromUTF32( item->GetTypeName() );
623 break;
624
626 layer_type = wxT( "Dielectric" );
627 // The option core or prepreg is not added here, as it creates constraints
628 // in build process, not necessary wanted.
629 if( sub_layer_count > 1 )
630 {
631 layer_name =
632 formatStringFromUTF32( wxString::Format( wxT( "dielectric layer %d - %d/%d" ),
633 item->GetDielectricLayerId(), sub_idx + 1, sub_layer_count ) );
634 }
635 else
636 layer_name = formatStringFromUTF32( wxString::Format(
637 wxT( "dielectric layer %d" ), item->GetDielectricLayerId() ) );
638 break;
639
640 default:
641 break;
642 }
643
644 layer_json["Type"] = layer_type;
645
646 if( item->IsColorEditable() && uptodate )
647 {
648 if( IsPrmSpecified( item->GetColor( sub_idx ) ) )
649 {
650 wxString colorName = item->GetColor( sub_idx );
651
652 if( colorName.StartsWith( wxT( "#" ) ) ) // This is a user defined color,
653 // not in standard color list.
654 {
655 // In job file a color can be given by its RGB values (0...255)
656 // like R<number><G<number>B<number> notation
657 wxColor color( COLOR4D( colorName ).ToColour() );
658 colorName.Printf( wxT( "R%dG%dB%d" ),
659 color.Red(),
660 color.Green(),
661 color.Blue() );
662 }
663 else
664 {
665 const std::vector<FAB_LAYER_COLOR>& color_list =
666 GetStandardColors( item->GetType() );
667
668 // Colors for dielectric use a color list that is mainly not normalized in
669 // job file names. So if a color is in the dielectric standard color list
670 // it can be a standard name or not.
671 // Colors for solder mask and silk screen use a mainly normalized
672 // color list, but this list can also contain not normalized colors.
673 // If not normalized, use the R<number><G<number>B<number> notation
674 for( const FAB_LAYER_COLOR& prm_color : color_list )
675 {
676 if( colorName == prm_color.GetName() )
677 {
678 colorName = prm_color.GetColorAsString();
679 break;
680 }
681 }
682 }
683
684 layer_json["Color"] = colorName;
685 }
686 }
687
688 if( item->IsThicknessEditable() && uptodate )
689 layer_json["Thickness"] = thickness;
690
691 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
692 {
693 if( item->HasMaterialValue() )
694 {
695 layer_json["Material"] = item->GetMaterial( sub_idx );
696
697 // These constrains are only written if the board has impedance controlled tracks.
698 // If the board is not impedance controlled, they are useless.
699 // Do not add constrains that create more expensive boards.
700
701 if( brd_stackup.m_HasDielectricConstrains )
702 {
703 // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
704 // a possible value
705 if( item->GetEpsilonR() > 1.0 )
706 layer_json["DielectricConstant"] = item->FormatEpsilonR( sub_idx );
707
708 // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
709 // a possible value
710 if( item->GetLossTangent() > 0.0 )
711 layer_json["LossTangent"] = item->FormatLossTangent( sub_idx );
712 }
713 }
714
715 // Copper layers IDs use only even values like 0, 2, 4 ...
716 // and first layer = F_Cu = 0, last layer = B_Cu = 2
717 // inner layers Ids are 4, 6 , 8 ...
718 PCB_LAYER_ID next_copper_layer = ( PCB_LAYER_ID )( last_copper_layer + 2 );
719
720 if( last_copper_layer == F_Cu )
721 next_copper_layer = In1_Cu;
722
723 // If the next_copper_layer is the last copper layer, the next layer id is B_Cu
724 if( next_copper_layer/2 >= m_pcb->GetCopperLayerCount() )
725 next_copper_layer = B_Cu;
726
727 wxString subLayerName;
728
729 if( sub_layer_count > 1 )
730 subLayerName.Printf( wxT( " (%d/%d)" ), sub_idx + 1, sub_layer_count );
731
732 wxString name = wxString::Format( wxT( "%s/%s%s" ),
733 formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
734 formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ),
735 subLayerName );
736
737 layer_json["Name"] = name;
738
739 // Add a comment ("Notes"):
740 wxString note;
741
742 note << wxString::Format( wxT( "Type: %s" ), layer_name.c_str() );
743
744 note << wxString::Format( wxT( " (from %s to %s)" ),
745 formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
746 formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
747
748 layer_json["Notes"] = note;
749 }
750 else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK
751 || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
752 {
753 if( item->HasMaterialValue() )
754 {
755 layer_json["Material"] = item->GetMaterial();
756
757 // These constrains are only written if the board has impedance controlled tracks.
758 // If the board is not impedance controlled, they are useless.
759 // Do not add constrains that create more expensive boards.
760 if( brd_stackup.m_HasDielectricConstrains )
761 {
762 // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
763 // a possible value
764 if( item->GetEpsilonR() > 1.0 )
765 layer_json["DielectricConstant"] = item->FormatEpsilonR();
766
767 // Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
768 // a possible value
769 if( item->GetLossTangent() > 0.0 )
770 layer_json["LossTangent"] = item->FormatLossTangent();
771 }
772 }
773
774 layer_json["Name"] = layer_name.c_str();
775 }
776 else
777 {
778 layer_json["Name"] = layer_name.c_str();
779 }
780
781 m_json["MaterialStackup"].insert( m_json["MaterialStackup"].end(), layer_json );
782 }
783 }
784}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_EDGE_CONNECTOR_BEVELLED
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_SILKSCREEN
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERPASTE
@ BS_ITEM_TYPE_SOLDERMASK
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
wxString GetBuildVersion()
Get the full KiCad version string.
Manage one layer needed to make a physical board.
wxString GetTypeName() const
int GetSublayersCount() const
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString GetColor(int aDielectricSubLayer=0) const
bool HasMaterialValue(int aDielectricSubLayer=0) const
PCB_LAYER_ID GetBrdLayerId() const
bool IsThicknessEditable() const
int GetThickness(int aDielectricSubLayer=0) const
BOARD_STACKUP_ITEM_TYPE GetType() const
wxString GetMaterial(int aDielectricSubLayer=0) const
wxString FormatEpsilonR(int aDielectricSubLayer=0) const
int GetDielectricLayerId() const
bool IsColorEditable() const
wxString FormatLossTangent(int aDielectricSubLayer=0) const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
int GetCount() const
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
BOARD_STACKUP_ITEM * GetStackupLayer(int aIndex)
bool m_EdgePlating
True if the edge board is plated.
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
void addJSONHeader()
Add the job file header in JSON format to m_JSONbuffer.
void addJSONMaterialStackup()
Add the Material Stackup section in JSON format to m_JSONbuffer This is the ordered list of stackup l...
void addJSONFilesAttributes()
Add the Files Attributes section in JSON format to m_JSONbuffer.
nlohmann::ordered_json m_json
bool CreateJobFile(const wxString &aFullFilename)
Creates a Gerber job file.
void addJSONGeneralSpecs()
Add the General Specs in JSON format to m_JSONbuffer.
bool WriteJSONJobFile(const wxString &aFullFilename)
Creates an Gerber job file in JSON format.
double mapValue(double aUiValue)
A helper function to convert a double in Pcbnew internal units to a JSON double value (in mm),...
const char * sideKeyValue(enum ONSIDE aValue)
void addJSONDesignRules()
Add the Design Rules section in JSON format to m_JSONbuffer.
std::string formatStringFromUTF32(const wxString &aText)
A helper function to convert a wxString ( therefore a Unicode text ) to a JSON compatible string (a e...
GERBER_JOBFILE_WRITER(BOARD *aPcb, REPORTER *aReporter=nullptr)
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Definition pad.h:55
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
Handle a list of polygons defining a copper zone.
Definition zone.h:73
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:62
The common library.
#define _(s)
wxString GbrMakeProjectGUIDfromString(const wxString &aText)
Build a project GUID using format RFC4122 Version 1 or 4 from the project name, because a KiCad proje...
wxString GbrMakeCreationDateAttributeString(GBR_NC_STRING_FORMAT aFormat)
Handle special data (items attributes) during plot.
@ GBR_NC_STRING_FORMAT_GBRJOB
Classes used to generate a Gerber job file in JSON.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ User_8
Definition layer_ids.h:131
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ User_6
Definition layer_ids.h:129
@ User_7
Definition layer_ids.h:130
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ User_5
Definition layer_ids.h:128
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ User_9
Definition layer_ids.h:132
@ F_Fab
Definition layer_ids.h:119
@ Margin
Definition layer_ids.h:113
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ Eco2_User
Definition layer_ids.h:110
@ In1_Cu
Definition layer_ids.h:66
@ User_3
Definition layer_ids.h:126
@ User_1
Definition layer_ids.h:124
@ B_SilkS
Definition layer_ids.h:101
@ User_4
Definition layer_ids.h:127
@ User_2
Definition layer_ids.h:125
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_ACTION
const std::vector< FAB_LAYER_COLOR > & GetStandardColors(BOARD_STACKUP_ITEM_TYPE aType)
VECTOR2I end
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
Definition of file extensions used in Kicad.