26#include <unordered_set>
67#include <wx/numformatter.h>
68#include <wx/mstream.h>
69#include <wx/xml/xml.h>
82enum class METADATA_SECTION
94 const char* m_display;
95 METADATA_SECTION m_section;
96 bool m_distance =
false;
100constexpr std::array<METADATA_FIELD, 15> METADATA_FIELDS = { {
101 {
"counter_bore",
"Counter Bore", METADATA_SECTION::HEADER },
102 {
"countersink",
"Counter Sink", METADATA_SECTION::HEADER },
103 {
"edge_connectors",
"Edge Connectors", METADATA_SECTION::HEADER },
104 {
"layer_count",
"Layer Count", METADATA_SECTION::HEADER },
105 {
"board_thickness",
"Board Thickness", METADATA_SECTION::REQUIREMENTS,
true },
106 {
"bottom_legend_color",
"Bottom Legend Color", METADATA_SECTION::REQUIREMENTS },
107 {
"bottom_soldermask_color",
"Bottom Soldermask Color", METADATA_SECTION::REQUIREMENTS },
108 {
"legend_sides",
"Legend Sides", METADATA_SECTION::REQUIREMENTS },
109 {
"plated_edge",
"Plated Edge", METADATA_SECTION::REQUIREMENTS },
110 {
"plated_slots",
"Plated Slots", METADATA_SECTION::REQUIREMENTS },
111 {
"soldermask_sides",
"Soldermask Sides", METADATA_SECTION::REQUIREMENTS },
112 {
"top_legend_color",
"Top Legend Color", METADATA_SECTION::REQUIREMENTS },
113 {
"top_soldermask_color",
"Top Soldermask Color", METADATA_SECTION::REQUIREMENTS },
114 {
"surface_finish_pads",
"Surface Finish Pads", METADATA_SECTION::MANUFACTURING },
115 {
"pressfit_technology",
"Pressfit Technology", METADATA_SECTION::ASSEMBLY },
125void WriteXmlFile(
ODB_TREE_WRITER& aWriter,
const char* aName, wxXmlDocument& aDocument,
const wxString& aError )
127 wxMemoryOutputStream xml;
129 if( !aDocument.Save( xml ) )
132 std::string output( xml.GetSize(),
'\0' );
133 xml.CopyTo( output.data(), output.size() );
135 file.
GetStream().write( output.data(), output.size() );
142double CopperThicknessMm(
int aThicknessIu )
144 return static_cast<double>( aThicknessIu ) /
pcbIUScale.mmToIU( 1.0 );
152double CopperWeightOz(
int aThicknessIu )
154 return CopperThicknessMm( aThicknessIu ) / 0.035;
165double CopperWeightValue(
const ODB_FORMAT& aFormat,
int aThicknessIu )
168 return CopperThicknessMm( aThicknessIu ) * 1000.0;
170 return CopperWeightOz( aThicknessIu );
182 catch(
const std::exception& e )
184 std::cerr << e.what() << std::endl;
195 { wxS(
"ODB_VERSION_MAJOR" ), wxS(
"8" ) },
196 { wxS(
"ODB_VERSION_MINOR" ), wxS(
"1" ) },
197 { wxS(
"ODB_SOURCE" ), wxS(
"KiCad EDA" ) },
198 { wxS(
"CREATION_DATE" ), wxDateTime::Now().Format(
"%Y%m%d.%H%M%S" ) },
199 { wxS(
"SAVE_DATE" ), wxDateTime::Now().Format(
"%Y%m%d.%H%M%S" ) },
200 { wxS(
"SAVE_APP" ), wxString::Format( wxS(
"KiCad EDA %s" ),
GetBuildVersion() ) } };
202 if( !
m_plugin->GetFormat().m_productModelName.IsEmpty() )
203 m_info.emplace_back( wxS(
"PRODUCT_MODEL_NAME" ),
m_plugin->GetFormat().m_productModelName );
222 if(
m_plugin->GetFormat().m_boardMetadata )
225 if( !
m_board->GetVariantNames().empty() )
235 if(
m_plugin->GetFormat().m_variantNames.m_listsFit )
236 attrlist.GetStream() <<
".variant_list="
237 <<
m_plugin->GetFormat().m_variantNames.Join(
m_board->GetVariantNames() ).ToStdString()
245 std::ostream& attrs = userAttrs.
GetStream();
246 attrs <<
"UNITS=" <<
m_plugin->GetFormat().m_unitsStr <<
'\n';
247 attrs <<
"TEXT {\nNAME=material\nENTITY=LAYER\nMIN_LEN=0\nMAX_LEN=64\nOPTIONS=\nGROUP=Product\n}\n";
249 for(
const char* side : {
"top",
"bottom" } )
251 attrs <<
"OPTION {\nNAME=post_machining_" << side
252 <<
"\nENTITY=FEATURE\nOPTIONS=countersink;counterbore\nGROUP=Fabrication\n}\n";
254 for(
const char* dimension : {
"diameter",
"depth" } )
256 attrs <<
"FLOAT {\nNAME=post_machining_" << side <<
'_' << dimension
257 <<
"\nENTITY=FEATURE\nMIN_VAL=0.0\nMAX_VAL=100000.0"
258 <<
"\nUNITS=MIL_MICRON\nGROUP=Fabrication\n}\n";
261 attrs <<
"FLOAT {\nNAME=post_machining_" << side
262 <<
"_angle\nENTITY=FEATURE\nMIN_VAL=0.0\nMAX_VAL=180.0\nGROUP=Fabrication\n}\n";
272 std::map<std::string, wxString> values;
274 auto yesNo = [](
bool aValue )
276 return aValue ? wxS(
"yes" ) : wxS(
"no" );
278 auto sides = [](
bool aTop,
bool aBottom,
const wxString& aNone ) -> wxString
281 return aBottom ? wxS(
"Both" ) : wxS(
"Top" );
284 return wxS(
"Bottom" );
294 values[
"layer_count"] = wxString::Format(
"%d",
m_board->GetCopperLayerCount() );
298 values[
"pressfit_technology"] = yesNo( drillFacts.
m_pressfit );
299 values[
"soldermask_sides"] =
305 auto addColor = [
this, &values](
const char* aName,
PCB_LAYER_ID aLayer )
307 wxString color =
m_plugin->GetFabStackup().NamedColor( aLayer );
309 if( !color.IsEmpty() )
310 values[aName] = color;
313 addColor(
"top_soldermask_color",
F_Mask );
314 addColor(
"bottom_soldermask_color",
B_Mask );
315 addColor(
"top_legend_color",
F_SilkS );
316 addColor(
"bottom_legend_color",
B_SilkS );
322 static const std::map<wxString, wxString> finishNames = {
323 { wxS(
"HAL SnPb" ), wxS(
"HASL" ) },
324 { wxS(
"HAL lead-free" ), wxS(
"Lead-Free HASL" ) },
325 { wxS(
"Immersion silver" ), wxS(
"Immersion Silver" ) },
326 { wxS(
"Immersion tin" ), wxS(
"White Tin" ) },
327 { wxS(
"OSP" ), wxS(
"OSP (Entek)" ) },
328 { wxS(
"None" ), wxS(
"Bare Cu" ) },
331 if(
auto it = finishNames.find( finish ); it != finishNames.end() )
334 if( std::all_of( finish.begin(), finish.end(),
335 []( wxUniChar aChar )
337 return aChar >= 32 && aChar <= 126;
339 values[
"surface_finish_pads"] = finish;
342 wxXmlDocument document;
343 wxXmlNode* root =
new wxXmlNode( wxXML_ELEMENT_NODE, wxS(
"metadata" ) );
344 document.SetRoot( root );
346 auto addElement = []( wxXmlNode* aParent,
const wxString& aName,
const wxString& aDescription )
348 wxXmlNode* node =
new wxXmlNode( wxXML_ELEMENT_NODE, aName );
350 if( !aDescription.IsEmpty() )
351 node->AddAttribute( wxS(
"description" ), aDescription );
353 aParent->AddChild( node );
357 constexpr std::array<std::pair<const char*, const char*>, 4> sectionInfo = { {
358 {
"Header",
"General Information" },
359 {
"Requirements",
"Board Requirements" },
360 {
"Manufacturing",
"Manufacturing Process" },
361 {
"Assembly",
"Assembly" },
363 std::array<wxXmlNode*, sectionInfo.size()> sections{};
364 wxXmlNode* step =
nullptr;
366 auto sectionNode = [&]( METADATA_SECTION aSection )
368 size_t index =
static_cast<size_t>( aSection );
370 if( !sections[
index] )
372 wxXmlNode* parent = root;
374 if(
index >=
static_cast<size_t>( METADATA_SECTION::MANUFACTURING ) )
378 wxXmlNode* steps = addElement( root, wxS(
"Steps" ), wxS(
"Steps" ) );
379 step = addElement( steps, wxS(
"Step" ), wxString() );
386 sections[
index] = addElement( parent, wxString::FromUTF8( sectionInfo[
index].first ),
387 wxString::FromUTF8( sectionInfo[
index].second ) );
390 return sections[
index];
393 for(
const METADATA_FIELD& field : METADATA_FIELDS )
395 auto value = values.find( field.m_name );
397 if( value == values.end() )
400 wxXmlNode* node = addElement( sectionNode( field.m_section ), wxS(
"node" ), wxString() );
401 node->AddAttribute( wxS(
"name" ), wxString::FromUTF8( field.m_name ) );
402 node->AddAttribute( wxS(
"display" ), wxString::FromUTF8( field.m_display ) );
403 node->AddAttribute( wxS(
"value" ), value->second );
405 if( field.m_distance )
406 node->AddAttribute( wxS(
"units" ), wxString::FromUTF8( format.
m_unitsStr ) );
409 WriteXmlFile( writer,
"metadata.xml", document,
_(
"Failed to generate ODB++ metadata." ) );
432 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.
GetList();
433 std::set<PCB_LAYER_ID> added_layers;
440 for(
int i = 0; i < stackup.
GetCount(); i++ )
444 for(
int sublayer_id = 0; sublayer_id < stackup_item->
GetSublayersCount(); sublayer_id++ )
448 if( ly_name.IsEmpty() )
491 if( added_layers.find( layer ) != added_layers.end() )
495 added_layers.insert( layer );
510 std::map<const BOARD_STACKUP_ITEM*, std::pair<PCB_LAYER_ID, PCB_LAYER_ID>> adjacentCopper;
525 for(
size_t probe =
index; probe-- > 0; )
560 return SECTION::COMPONENTS;
565 return SECTION::DRILL_ROUT;
569 return SECTION::MISC_FAB;
571 switch( aLayer.m_type )
581 default:
return SECTION::DOCUMENTATION;
585 std::set<PCB_LAYER_ID> excluded;
589 if( !
override.m_include )
590 excluded.insert(
override.m_layer );
596 return !format.
Includes( sectionFor( aLayer ) )
603 std::set<wxString> usedNames;
606 usedNames.insert( layer.m_layerName );
617 if( !
override.m_odbName.IsEmpty() )
621 if(
name.IsEmpty() || (
name != layer.m_layerName && usedNames.contains(
name ) ) )
623 m_plugin->Report(
_(
"ODB++ layer name override is empty or collides; using automatic name." ),
628 usedNames.erase( layer.m_layerName );
629 layer.m_layerName =
name;
630 usedNames.insert(
name );
634 if( !
override.m_odbType.IsEmpty() )
636 std::string type =
override.m_odbType.ToStdString();
638 auto it = std::find_if( allowed.begin(), allowed.end(),
641 return ODB::Enum2String( aType ) == type;
644 if( it != allowed.end() )
647 m_plugin->Report(
_(
"Invalid ODB++ layer type override; using automatic type." ),
656 std::map<PCB_LAYER_ID, wxString> namesByLayer;
662 namesByLayer[layer.m_info.m_layer] = layer.m_layerName;
686 if( namesByLayer.contains( start ) && namesByLayer.contains(
end ) )
688 aLayer.
m_span = { namesByLayer.at( start ), namesByLayer.at(
end ) };
692 wxCHECK_MSG( filtered,
false, wxS(
"ODB++ span layer has no matrix row" ) );
700 aLayer.
m_span = { wxEmptyString, wxEmptyString };
704 m_plugin->Report(
_(
"ODB++ partial drill layer omitted because a span layer is excluded." ),
715 std::map<PCB_LAYER_ID, uint32_t> copperIds;
723 copperIds[layer.m_info.m_layer] = layer.m_uid;
739 if(
auto it = copperIds.find( copper ); it != copperIds.end() )
740 layer.
m_ref = it->second;
750 if(
auto it = copperIds.find(
top ); it != copperIds.end() )
753 if(
auto it = copperIds.find( bottom ); it != copperIds.end() )
759 for(
size_t top = i;
top-- > 0; )
768 for(
size_t bottom = i + 1; bottom <
m_matrixLayers.size(); ++bottom )
778 std::vector<ODB_LAYER_NAME>& names =
m_plugin->GetLayerNameList();
784 names.push_back( layer.m_info );
785 names.back().m_name = layer.m_layerName;
875 switch(
m_board->GetLayerType( aLayer ) )
901 std::map<DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
903 std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
906 drill_layers.clear();
909 bool has_pth_layer =
false;
910 bool has_npth_layer =
false;
914 for(
PAD*
pad : fp->Pads() )
922 std::map<DRILL_SPAN, std::unordered_set<BOARD_ITEM*>> seen;
926 auto addOperation = [&](
const DRILL_OPERATION& aOperation,
bool aSlot )
930 bool fromBack = !layer.m_Span.m_IsBackdrill && item->
Type() ==
PCB_VIA_T
932 DRILL_SPAN span( fromBack ? layer.m_Span.BottomLayer() : layer.m_Span.DrillStartLayer(),
933 fromBack ? layer.m_Span.TopLayer() : layer.m_Span.DrillEndLayer(),
934 layer.m_Span.m_IsBackdrill, aOperation.
m_NotPlated );
938 slot_holes[span.
Pair()].push_back( item );
942 if( seen[span].insert( item ).second )
943 drill_layers[span].push_back( item );
948 has_pth_layer |= !op.
m_NotPlated && !layer.m_Span.m_IsBackdrill;
949 addOperation( op,
false );
953 addOperation( op,
true );
959 drill_layers[npthSpan];
965 drill_layers[platedSpan];
968 int backdrillIndex = 1;
970 auto assignName = [&](
const DRILL_SPAN& aSpan )
974 if( aSpan.m_IsBackdrill )
976 name.Printf( wxT(
"drill%d" ), backdrillIndex++ );
980 wxString platedLabel = aSpan.m_IsNonPlatedFile ? wxT(
"non-plated" ) : wxT(
"plated" );
981 name.Printf( wxT(
"drill_%s_%s-%s" ), platedLabel,
m_board->GetLayerName( aSpan.TopLayer() ),
982 m_board->GetLayerName( aSpan.BottomLayer() ) );
988 auto InitDrillMatrix = [&](
const DRILL_SPAN& aSpan )
990 wxString dLayerName = assignName( aSpan );
1002 if( aSpan.m_IsBackdrill )
1008 for(
const auto& entry : drill_layers )
1009 InitDrillMatrix( entry.first );
1040 auto& auxilliary_layers =
m_plugin->GetAuxilliaryLayerItemsMap();
1041 auxilliary_layers.clear();
1059 via->BottomLayer() )]
1066 via->BottomLayer() )]
1100 wxString featureName =
"";
1101 switch( std::get<0>( aLayerPair ) )
1111 wxString dLayerName;
1115 dLayerName = wxString::Format(
"%s_%s-%s", featureName,
1116 m_board->GetLayerName( std::get<1>( aLayerPair ) ),
1117 m_board->GetLayerName( std::get<2>( aLayerPair ) ) );
1121 if(
m_board->IsFrontLayer( std::get<1>( aLayerPair ) ) )
1122 dLayerName = wxString::Format(
"%s_front", featureName );
1123 else if(
m_board->IsBackLayer( std::get<1>( aLayerPair ) ) )
1124 dLayerName = wxString::Format(
"%s_back", featureName );
1135 ? std::get<1>( aLayerPair )
1142 matrix.
m_span.emplace( std::make_pair(
1145 m_board->GetLayerName( std::get<2>( aLayerPair ) ) ) ) );
1151 for(
const auto& [layer_pair, vec] : auxilliary_layers )
1153 InitAuxMatrix( layer_pair );
1160 std::vector<wxString> bases;
1164 bases.push_back( layer.m_layerName );
1195 if( layer.m_addType.has_value() )
1204 if( layer.m_diType.has_value() )
1208 if( !layer.m_dielectricName.IsEmpty() )
1214 if( layer.m_cuBottom )
1215 twriter.
WriteEquationLine(
"CU_BOTTOM",
static_cast<int>( *layer.m_cuBottom ) );
1221 if( layer.m_span.has_value() )
1239 wxString units = wxString::FromUTF8( format.
m_unitsStr.c_str() );
1241 wxXmlDocument document;
1243 auto add = []( wxXmlNode* aParent,
const wxString& aName )
1245 wxXmlNode* node =
new wxXmlNode( wxXML_ELEMENT_NODE, aName );
1248 aParent->AddChild( node );
1253 wxXmlNode* root = add(
nullptr, wxS(
"StackupFile" ) );
1254 document.SetRoot( root );
1255 root->AddAttribute( wxS(
"Version" ), wxS(
"8.1" ) );
1256 root->AddAttribute( wxS(
"DefaultUnits" ), units );
1258 wxXmlNode* eda = add( root, wxS(
"EdaData" ) );
1259 eda->AddAttribute( wxS(
"CompanyName" ),
m_board->GetTitleBlock().GetCompany() );
1260 wxXmlNode* specs = add( eda, wxS(
"Specs" ) );
1261 wxXmlNode* spec = add( specs, wxS(
"Spec" ) );
1262 spec->AddAttribute( wxS(
"SpecName" ), stepName );
1263 wxXmlNode* stackupNode = add( eda, wxS(
"Stackup" ) );
1264 stackupNode->AddAttribute( wxS(
"StackupName" ), stepName );
1266 stackupNode->AddAttribute( wxS(
"Units" ), units );
1267 bool maskThickness =
false;
1275 stackupNode->AddAttribute( wxS(
"WhereMeasured" ), maskThickness ? wxS(
"MASK" ) : wxS(
"METAL" ) );
1276 wxXmlNode*
group = add( stackupNode, wxS(
"Group" ) );
1277 group->AddAttribute( wxS(
"GroupName" ), stepName );
1284 wxXmlNode* layer = add(
group, wxS(
"Layer" ) );
1285 layer->AddAttribute( wxS(
"LayerName" ), row.m_layerName );
1286 layer->AddAttribute( wxS(
"LayerType" ),
1290 layer->AddAttribute( wxS(
"Side" ), wxS(
"INNER" ) );
1291 else if(
m_board->IsFrontLayer( row.m_info.m_layer ) )
1292 layer->AddAttribute( wxS(
"Side" ), wxS(
"TOP" ) );
1293 else if(
m_board->IsBackLayer( row.m_info.m_layer ) )
1294 layer->AddAttribute( wxS(
"Side" ), wxS(
"BOTTOM" ) );
1296 layer->AddAttribute( wxS(
"Side" ), wxS(
"INNER" ) );
1300 if( !row.m_span->first.IsEmpty() )
1301 layer->AddAttribute( wxS(
"MechStartLayerName" ), row.m_span->first );
1303 if( !row.m_span->second.IsEmpty() )
1304 layer->AddAttribute( wxS(
"MechEndLayerName" ), row.m_span->second );
1308 int sublayer = row.m_info.m_sublayer;
1313 wxXmlNode* material = add( spec, wxS(
"Material" ) );
1314 material->AddAttribute( wxS(
"MaterialName" ), row.m_layerName );
1318 wxXmlNode* conductor = add( material, wxS(
"Conductor" ) );
1319 conductor->AddAttribute( wxS(
"ConductorType" ), wxS(
"COPPER" ) );
1320 conductor->AddAttribute( wxS(
"CopperWeight_oz_ft2" ),
1325 wxXmlNode* dielectric = add( material, wxS(
"Dielectric" ) );
1329 dielectric->AddAttribute( wxS(
"DielectricType" ), wxS(
"OTHER" ) );
1330 dielectric->AddAttribute( wxS(
"OtherSubType" ), wxS(
"SOLDER_MASK" ) );
1334 dielectric->AddAttribute( wxS(
"DielectricType" ),
1337 : wxS(
"UNDEFINED" ) );
1340 wxString reference = item->
GetMaterial( sublayer );
1343 dielectric->AddAttribute( wxS(
"MaterialReference" ), reference );
1348 if( hasDk || hasDf )
1350 wxXmlNode* properties = add( dielectric, wxS(
"Properties" ) );
1351 properties->AddAttribute( wxS(
"PropertyName" ), wxS(
"default" ) );
1352 wxXmlNode*
property = add( properties, wxS(
"Property" ) );
1355 if( frequency > 0.0 )
1357 property->AddAttribute( wxS(
"FrequencyVal" ),
ODB::Double2String( format, frequency / 1e9 ) );
1358 property->AddAttribute( wxS(
"Units" ), wxS(
"GHz" ) );
1363 property->AddAttribute( wxS(
"DielectricConstant_Dk" ),
1369 property->AddAttribute( wxS(
"LossTangent_Df" ),
1375 wxXmlNode* thickness = add( material, wxS(
"Default_Thickness" ) );
1377 thickness->AddAttribute( wxS(
"Units" ), units );
1379 wxXmlNode* ref = add( layer, wxS(
"SpecRef" ) );
1380 ref->AddAttribute( wxS(
"MaterialSpecName" ), stepName );
1381 wxXmlNode* named = add( ref, wxS(
"Material" ) );
1382 named->AddAttribute( wxS(
"MaterialName" ), row.m_layerName );
1385 WriteXmlFile( writer,
"stackup.xml", document,
_(
"Failed to generate ODB++ stackup." ) );
1390 const std::map<
int, std::vector<BOARD_ITEM*>>& aMap,
1439 std::vector<BOARD_ITEM*>& vec =
m_layerItems[net->GetNetCode()];
1444 std::stable_sort( vec.begin(), vec.end(),
FabItemLess );
1454 bool hasOutline =
m_board->GetBoardPolygonOutlines( outlines,
false,
nullptr,
true );
1458 m_plugin->Report(
_(
"Board outline is not closed; ODB++ rout layer has no board contour." ),
1463 bool edgePlated =
m_plugin->GetFabStackup().Stackup().m_EdgePlating;
1465 for(
int island = 0; hasOutline && island < outlines.
OutlineCount(); ++island )
1469 for(
int hole = 0; hole < outlines.
HoleCount( island ); ++hole )
1473 for(
const auto& [span, items] :
m_plugin->GetSlotHolesMap() )
1496 std::map<DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
1498 std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
1501 wxCHECK_RET(
m_layer.m_drillSpan.has_value(),
"Drill matrix row has no span" );
1516 if( !isBackdrillLayer )
1521 auto slotIt = slot_holes.find( matchedSpan.
Pair() );
1523 if( slotIt != slot_holes.end() )
1534 if( isNPTHLayer != padIsNPTH )
1538 padIsNPTH ? wxT(
"NON_PLATED" ) : wxT(
"PLATED" ),
1539 std::min(
pad->GetDrillSize().x,
pad->GetDrillSize().y ),
1541 ? wxT(
"PRESS_FIT" ) : wxT(
"STANDARD" ) );
1548 auto drillIt = drill_layers.find( matchedSpan );
1550 if( drillIt != drill_layers.end() )
1562 if( isBackdrillLayer )
1564 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), wxT(
"NON_PLATED" ),
1568 else if( isNonPlatedLayer )
1570 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), wxT(
"NON_PLATED" ),
1578 : wxT(
"STANDARD" ) );
1593 if( isNPTHLayer && !padIsNPTH )
1596 if( !isNonPlatedLayer && padIsNPTH )
1601 wxString typeLabel = ( padIsNPTH || isNonPlatedLayer ) ? wxT(
"NON_PLATED" ) : wxT(
"PLATED" );
1602 wxString type2 = isBackdrillLayer ? wxT(
"BLIND" ) : wxT(
"STANDARD" );
1605 type2 = wxT(
"PRESS_FIT" );
1607 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), typeLabel, drillSize, type2 );
1617 auto& auxilliary_layers =
m_plugin->GetAuxilliaryLayerItemsMap();
1619 wxCHECK_RET(
m_layer.m_auxKey.has_value(),
"Auxiliary matrix row has no key" );
1626 auto auxIt = auxilliary_layers.find( *
m_layer.m_auxKey );
1628 if( auxIt == auxilliary_layers.end() )
1651 layer_entity_ptr->InitEntityData();
1684 m_featuresMgr->GenerateFeatureFile( fileproxy.GetStream() );
1692 std::ostream& ost = fileproxy.
GetStream();
1693 ost <<
"UNITS=" <<
m_plugin->GetFormat().m_unitsStr <<
'\n';
1698 ost <<
".drill_layer_direction=bottom2top" << std::endl;
1709 const std::vector<ODB_LAYER_NAME>& layers =
m_plugin->GetLayerNameList();
1710 auto row = std::find_if( layers.begin(), layers.end(),
1713 return aLayer.m_role == ODB_LAYER_ROLE::BOARD_LAYER
1714 && aLayer.m_layer == stop;
1717 if( row != layers.end() )
1718 ost <<
".backdrill_penetrate_stop_layer=" << row->m_name.ToStdString() << std::endl;
1726 int sublayer =
m_layer.m_sublayer;
1736 const double copperWeight = CopperWeightValue(
m_plugin->GetFormat(), thickness );
1738 ost <<
".copper_weight="
1745 double epsilonR = stackupItem->
GetEpsilonR( sublayer );
1747 if( epsilonR > 0.0 )
1757 if( lossTangent > 0.0 )
1763 wxString material = stackupItem->
GetMaterial( sublayer );
1765 if( !material.IsEmpty() )
1777 m_tools.value().GenerateFile( fileproxy.GetStream() );
1800 std::array<wxString, 2> componentNames;
1805 componentNames[layer.m_componentSide ==
B_Cu ? 1 : 0] = layer.m_name;
1812 std::shared_ptr<FOOTPRINT> fp_pkg =
m_edaData.GetEdaFootprints().at( j );
1818 bool hasCourtyard = std::any_of( fp->GraphicalItems().begin(), fp->GraphicalItems().end(),
1821 return item->IsOnLayer( F_CrtYd ) || item->IsOnLayer( B_CrtYd );
1829 if( writeComponents )
1831 auto iter =
m_layerEntityMap.find( componentNames[fp->IsFlipped() ? 1 : 0] );
1834 comp = &iter->second->InitComponentData( fp, eda_pkg );
1837 for(
int i = 0; i < (int) fp->Pads().size(); ++i )
1839 PAD*
pad = fp->Pads()[i];
1847 m_plugin->GetPadSubnetMap().emplace(
pad, &subnet );
1854 auto& toep =
comp->m_toeprints.emplace_back( pin_ref );
1856 toep.m_net_num = eda_net.
m_index;
1857 toep.m_subnet_num = subnet.
m_index;
1862 (
ANGLE_360 -
pad->GetOrientation() ).Normalize().AsDegrees() );
1864 if(
pad->IsFlipped() )
1865 toep.m_mirror = wxT(
"M" );
1867 toep.m_mirror = wxT(
"N" );
1881 m_plugin->GetViaTraceSubnetMap().emplace( track, subnet );
1897 m_plugin->GetPlaneSubnetMap().emplace( std::piecewise_construct,
1898 std::forward_as_tuple( layer, zone ),
1899 std::forward_as_tuple( &subnet ) );
1932 int thickness =
m_plugin->GetFabStackup().Thickness();
1934 attrlist.GetStream() <<
"UNITS=" << format.
m_unitsStr <<
'\n';
1935 attrlist.GetStream() <<
".board_thickness="
1940 if( !selected.IsEmpty() )
1942 attrlist.GetStream() <<
".current_variant=" << selected.ToStdString() <<
'\n';
1956 if( !
m_board->GetBoardPolygonOutlines( board_outline,
true ) )
1957 wxLogTrace(
traceOdbppIo,
"Failed to get board outline" );
1961 double largestArea = count > 0 ? board_outline.
Outline( 0 ).
Area() : 0.0;
1963 for(
int ii = 1; ii < count; ++ii )
1967 if( area > largestArea )
1974 if( !
m_profile->AddContour( board_outline, largest ) )
1975 wxLogTrace(
traceOdbppIo,
"Failed to add polygon to profile" );
1977 m_profile->GenerateProfileFeatures( fileproxy.GetStream() );
1989 {
"X_ORIGIN",
"0" },
1990 {
"Y_ORIGIN",
"0" },
1991 {
"TOP_ACTIVE",
"0" },
1992 {
"BOTTOM_ACTIVE",
"0" },
1993 {
"RIGHT_ACTIVE",
"0" },
1994 {
"LEFT_ACTIVE",
"0" },
1995 {
"AFFECTING_BOM",
"" },
1996 {
"AFFECTING_BOM_CHANGED",
"0" },
2001 for(
const auto& [key, value] :
m_stephdr )
2016 layerEntity->GenerateFiles( writer );
2025 std::set<int> m_nets;
2027 size_t m_featureIndex;
2031std::vector<SHORT_SHAPE> CollectNetTieShorts(
BOARD* aBoard,
PCB_IO_ODBPP* aPlugin )
2033 std::vector<SHORT_SHAPE> shortShapes;
2037 const auto* shape =
static_cast<const PCB_SHAPE*
>( shapeLayer.first );
2041 auto copper = shape->GetEffectiveShape( layer );
2042 std::set<int> netCodes;
2044 bool invalidGroup =
false;
2049 if(
pad->GetNetCode() <= 0 || !
pad->GetLayerSet()[layer]
2050 || !
pad->GetEffectiveShape( layer )->Collide( copper.get() ) )
2055 auto padGroup = groups.find(
pad->GetNumber() );
2056 int currentGroup = padGroup == groups.end() ? -1 : padGroup->second;
2058 if( currentGroup < 0 || (
group >= 0 &&
group != currentGroup ) )
2059 invalidGroup =
true;
2061 group = currentGroup;
2062 netCodes.insert(
pad->GetNetCode() );
2067 wxLogTrace(
traceOdbppIo, wxT(
"Net tie shape touches pads outside one group" ) );
2071 if( netCodes.size() >= 2 )
2072 shortShapes.push_back( { std::move( netCodes ), feature.first, feature.second } );
2080 bool hasCopperShape = std::any_of( footprint->
GraphicalItems().begin(),
2084 return item->Type() == PCB_SHAPE_T
2085 && ( item->GetLayerSet() & LSET::AllCuMask() ).any();
2088 if( !hasCopperShape )
2089 wxLogTrace(
traceOdbppIo, wxT(
"Net tie has no copper shape for shortf" ) );
2092 std::sort( shortShapes.begin(), shortShapes.end(), [](
const SHORT_SHAPE&
left,
const SHORT_SHAPE&
right )
2093 { return std::tie( left.m_layer, left.m_featureIndex )
2094 < std::tie( right.m_layer, right.m_featureIndex ); } );
2103 std::vector<SHORT_SHAPE> shortShapes;
2105 if(
m_plugin->GetFormat().m_writeEdaNets )
2108 if( !shortShapes.empty() )
2112 for(
const SHORT_SHAPE& shape : shortShapes )
2113 m_edaData.AddShort( shape.m_nets, shape.m_layer, shape.m_featureIndex,
m_plugin->NewUid() );
2144 catch(
const std::exception& e )
2146 std::cerr << e.what() << std::endl;
2158 std::shared_ptr<ODB_LAYER_ENTITY> layerEntity =
constexpr EDA_IU_SCALE pcbIUScale
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERMASK
wxString GetBuildVersion()
Get the full KiCad version string.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
FOOTPRINT * GetParentFootprint() const
Manage one layer needed to make a physical board.
wxString GetTypeName() const
int GetSublayersCount() const
double GetEpsilonR(int aDielectricSubLayer=0) const
bool HasEpsilonRValue() const
wxString GetLayerName() const
bool HasLossTangentValue() 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
double GetSpecFreq(int aDielectricSubLayer=0) const
int GetDielectricLayerId() const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
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.
const FOOTPRINTS & Footprints() const
T & AddSubnet(Args &&... args)
const std::shared_ptr< PIN > GetEdaPkgPin(size_t aPadIndex) const
KICAD_T Type() const
Returns the type of object.
const FAB_DRILL_FACTS & Facts() const
Board items grouped by authored layer and net, sorted by FabItemLess.
Board stackup snapshot synchronized without changing the source board.
const BOARD_STACKUP_ITEM * ItemForLayer(PCB_LAYER_ID aLayer) const
Handle the data for a net.
Container for NETINFO_ITEM elements, which are the nets.
static std::map< T, std::string > & GetMap()
virtual std::string GetEntityName()=0
virtual bool CreateDirectoryTree(ODB_TREE_WRITER &writer)
ODB_ENTITY_BASE(BOARD *aBoard, PCB_IO_ODBPP *aPlugin)
std::ostream & GetStream()
void GenFeatures(ODB_TREE_WRITER &writer)
void GenComponents(ODB_TREE_WRITER &writer)
void InitAuxilliaryData()
virtual void GenerateFiles(ODB_TREE_WRITER &writer) override
std::optional< COMPONENTS_MANAGER > m_components
std::optional< ODB_DRILL_TOOLS > m_tools
void GenTools(ODB_TREE_WRITER &writer)
std::map< int, std::vector< BOARD_ITEM * > > m_layerItems
ODB_LAYER_ENTITY(BOARD *aBoard, PCB_IO_ODBPP *aPlugin, const std::map< int, std::vector< BOARD_ITEM * > > &aMap, const ODB_LAYER_NAME &aLayer)
ODB_COMPONENT & InitComponentData(const FOOTPRINT *aFp, const EDA_DATA::PACKAGE &aPkg)
virtual void InitEntityData() override
void GenAttrList(ODB_TREE_WRITER &writer)
std::unique_ptr< FEATURES_MANAGER > m_featuresMgr
void GenerateStackupFile(ODB_TREE_WRITER &writer)
void AddStep(const wxString &aStepName)
void InitMatrixLayerData()
std::vector< MATRIX_LAYER > m_matrixLayers
void AddMatrixLayerField(MATRIX_LAYER &aMLayer, PCB_LAYER_ID aLayer)
virtual void GenerateFiles(ODB_TREE_WRITER &writer) override
void AddRoutMatrixLayer()
std::map< wxString, std::pair< unsigned int, uint32_t > > m_matrixSteps
void AddAuxilliaryMatrixLayer()
void EnsureUniqueLayerNames()
void AddDrillMatrixLayer()
virtual void InitEntityData() override
void AddCOMPMatrixLayer(PCB_LAYER_ID aCompSide)
void GenerateAttrListFile(ODB_TREE_WRITER &writer)
void GenerateUserAttrFile(ODB_TREE_WRITER &writer)
ODB_MISC_ENTITY(BOARD *aBoard, PCB_IO_ODBPP *aPlugin)
std::vector< std::pair< wxString, wxString > > m_info
virtual void GenerateFiles(ODB_TREE_WRITER &writer) override
void GenerateMetadataFile(ODB_TREE_WRITER &writer)
virtual std::string GetEntityName() override
void GenerateProfileFile(ODB_TREE_WRITER &writer)
void GenerateStepHeaderFile(ODB_TREE_WRITER &writer)
void GenerateEdaFiles(ODB_TREE_WRITER &writer)
virtual void InitEntityData() override
static constexpr const char * STEP_NAME
void GenerateAttrListFile(ODB_TREE_WRITER &writer)
void GenerateLayerFiles(ODB_TREE_WRITER &writer)
std::map< wxString, std::shared_ptr< ODB_LAYER_ENTITY > > m_layerEntityMap
std::unique_ptr< FEATURES_MANAGER > m_profile
std::vector< std::pair< wxString, wxString > > m_stephdr
virtual bool CreateDirectoryTree(ODB_TREE_WRITER &writer) override
void GenerateNetlistsFiles(ODB_TREE_WRITER &writer)
virtual void GenerateFiles(ODB_TREE_WRITER &writer) override
void WriteEquationLine(const std::string &var, int value)
ARRAY_PROXY MakeArrayProxy(const std::string &aStr)
void write_line_enum(const std::string &var, const T &value)
const wxString GetRootPath() const
void CreateEntityDirectory(const wxString &aPareDir, const wxString &aSubDir=wxEmptyString)
ODB_FILE_WRITER CreateFileProxy(const wxString &aFileName)
void SetCurrentPath(const wxString &aDir)
const wxString GetCurrentPath() const
const std::map< std::pair< const BOARD_ITEM *, PCB_LAYER_ID >, std::pair< wxString, size_t > > & GetNetTieFeatures() const
double Area(bool aAbsolute=true) const
Return the area of this chain.
Represent a set of closed polygons.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
static constexpr EDA_ANGLE ANGLE_360
PCB_LAYER_ID FabBackdrillMustNotCut(const LSEQ &aCopperStack, PCB_LAYER_ID aStart, PCB_LAYER_ID aMustCut)
aMustCut is DRILL_PROPS.end; returns the next enabled copper past it away from aStart,...
bool FabItemLess(const BOARD_ITEM *aFirst, const BOARD_ITEM *aSecond)
Order by footprint UUID so feature output is independent of allocation order.
bool FabLayerHasVisibleItems(const BOARD &aBoard, PCB_LAYER_ID aLayer)
True if the board draws visible text, graphics or zones on aLayer.
FAB_PAD_ROLE GetFabPadRole(const PAD &aPad)
Explicit fabrication properties take precedence over the unnumbered NPTH fallback.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
const wxChar *const traceOdbppIo
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Hashing functions for EDA_ITEMs.
@ REL_COORD
Use coordinates relative to the parent object.
bool IsCopperLayerLowerThan(PCB_LAYER_ID aLayerA, PCB_LAYER_ID aLayerB)
Return true if copper aLayerA is placed lower than aLayerB, false otherwise.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
bool IsValidLayer(int aLayerId)
Test whether a given integer is a valid layer index, i.e.
SECTION
Schema sections of the IPC-2581C function mode table, Table 4 p 80.
std::string Enum2String(T value)
std::vector< ODB_TYPE > OverridableLayerTypes(PCB_LAYER_ID aLayer)
Matrix types a layer override may assign to aLayer.
std::pair< wxString, wxString > AddXY(const ODB_FORMAT &aFormat, const VECTOR2I &aVec)
std::vector< wxString > UniqueNames(const std::vector< wxString > &aBases, const std::set< wxString > &aReserved, size_t aMaxLength)
wxString Data2String(const ODB_FORMAT &aFormat, double aVal)
wxString GenLegalEntityName(const wxString &aStr)
bool HasShownModel(const FOOTPRINT *aFp)
wxString Double2String(const ODB_FORMAT &aFormat, double aVal)
std::tuple< ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID > ODB_AUX_LAYER_KEY
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ PTH
Plated through hole pad.
static bool isCopper(const PNS::ITEM *aItem)
One machining action, which is also one NC hit and one tool assignment.
PCB_LAYER_ID m_BottomLayer
BOARD_ITEM * m_SourceItem
non-owning, valid only for this enumeration
DRILL_LAYER_PAIR Pair() const
PCB_LAYER_ID m_StartLayer
Board-wide hole facts that a fabrication product states outright.
wxString SelectedName(const wxString &aName) const
PCB_LAYER_ID m_componentSide
std::optional< DRILL_SPAN > m_drillSpan
const BOARD_STACKUP_ITEM * m_stackupItem
std::optional< ODB_AUX_LAYER_TYPE > m_auxType
std::optional< ODB_AUX_LAYER_KEY > m_auxKey
wxString m_dielectricName
std::optional< ODB_SUBTYPE > m_addType
std::optional< ODB_DIELECTRIC_TYPE > m_diType
std::optional< uint32_t > m_cuBottom
std::optional< uint32_t > m_ref
std::optional< uint32_t > m_cuTop
std::optional< std::pair< wxString, wxString > > m_span
KIBIS top(path, &reporter)
const SHAPE_LINE_CHAIN chain
wxLogTrace helper definitions.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_PAD_T
class PAD, a pad in a footprint