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() );
139double CopperWeightOz(
int aThickness )
141 double thicknessMm =
static_cast<double>( aThickness ) /
pcbIUScale.mmToIU( 1.0 );
142 return thicknessMm / 0.035;
154 catch(
const std::exception& e )
156 std::cerr << e.what() << std::endl;
167 { wxS(
"ODB_VERSION_MAJOR" ), wxS(
"8" ) },
168 { wxS(
"ODB_VERSION_MINOR" ), wxS(
"1" ) },
169 { wxS(
"ODB_SOURCE" ), wxS(
"KiCad EDA" ) },
170 { wxS(
"CREATION_DATE" ), wxDateTime::Now().Format(
"%Y%m%d.%H%M%S" ) },
171 { wxS(
"SAVE_DATE" ), wxDateTime::Now().Format(
"%Y%m%d.%H%M%S" ) },
172 { wxS(
"SAVE_APP" ), wxString::Format( wxS(
"KiCad EDA %s" ),
GetBuildVersion() ) } };
174 if( !
m_plugin->GetFormat().m_productModelName.IsEmpty() )
175 m_info.emplace_back( wxS(
"PRODUCT_MODEL_NAME" ),
m_plugin->GetFormat().m_productModelName );
194 if(
m_plugin->GetFormat().m_boardMetadata )
197 if( !
m_board->GetVariantNames().empty() )
207 if(
m_plugin->GetFormat().m_variantNames.m_listsFit )
208 attrlist.GetStream() <<
".variant_list="
209 <<
m_plugin->GetFormat().m_variantNames.Join(
m_board->GetVariantNames() ).ToStdString()
217 std::ostream& attrs = userAttrs.
GetStream();
218 attrs <<
"UNITS=" <<
m_plugin->GetFormat().m_unitsStr <<
'\n';
219 attrs <<
"TEXT {\nNAME=material\nENTITY=LAYER\nMIN_LEN=0\nMAX_LEN=64\nOPTIONS=\nGROUP=Product\n}\n";
221 for(
const char* side : {
"top",
"bottom" } )
223 attrs <<
"OPTION {\nNAME=post_machining_" << side
224 <<
"\nENTITY=FEATURE\nOPTIONS=countersink;counterbore\nGROUP=Fabrication\n}\n";
226 for(
const char* dimension : {
"diameter",
"depth" } )
228 attrs <<
"FLOAT {\nNAME=post_machining_" << side <<
'_' << dimension
229 <<
"\nENTITY=FEATURE\nMIN_VAL=0.0\nMAX_VAL=100000.0"
230 <<
"\nUNITS=MIL_MICRON\nGROUP=Fabrication\n}\n";
233 attrs <<
"FLOAT {\nNAME=post_machining_" << side
234 <<
"_angle\nENTITY=FEATURE\nMIN_VAL=0.0\nMAX_VAL=180.0\nGROUP=Fabrication\n}\n";
244 std::map<std::string, wxString> values;
246 auto yesNo = [](
bool aValue )
248 return aValue ? wxS(
"yes" ) : wxS(
"no" );
250 auto sides = [](
bool aTop,
bool aBottom,
const wxString& aNone ) -> wxString
253 return aBottom ? wxS(
"Both" ) : wxS(
"Top" );
256 return wxS(
"Bottom" );
266 values[
"layer_count"] = wxString::Format(
"%d",
m_board->GetCopperLayerCount() );
270 values[
"pressfit_technology"] = yesNo( drillFacts.
m_pressfit );
271 values[
"soldermask_sides"] =
277 auto addColor = [
this, &values](
const char* aName,
PCB_LAYER_ID aLayer )
279 wxString color =
m_plugin->GetFabStackup().NamedColor( aLayer );
281 if( !color.IsEmpty() )
282 values[aName] = color;
285 addColor(
"top_soldermask_color",
F_Mask );
286 addColor(
"bottom_soldermask_color",
B_Mask );
287 addColor(
"top_legend_color",
F_SilkS );
288 addColor(
"bottom_legend_color",
B_SilkS );
294 static const std::map<wxString, wxString> finishNames = {
295 { wxS(
"HAL SnPb" ), wxS(
"HASL" ) },
296 { wxS(
"HAL lead-free" ), wxS(
"Lead-Free HASL" ) },
297 { wxS(
"Immersion silver" ), wxS(
"Immersion Silver" ) },
298 { wxS(
"Immersion tin" ), wxS(
"White Tin" ) },
299 { wxS(
"OSP" ), wxS(
"OSP (Entek)" ) },
300 { wxS(
"None" ), wxS(
"Bare Cu" ) },
303 if(
auto it = finishNames.find( finish ); it != finishNames.end() )
306 if( std::all_of( finish.begin(), finish.end(),
307 []( wxUniChar aChar )
309 return aChar >= 32 && aChar <= 126;
311 values[
"surface_finish_pads"] = finish;
314 wxXmlDocument document;
315 wxXmlNode* root =
new wxXmlNode( wxXML_ELEMENT_NODE, wxS(
"metadata" ) );
316 document.SetRoot( root );
318 auto addElement = []( wxXmlNode* aParent,
const wxString& aName,
const wxString& aDescription )
320 wxXmlNode* node =
new wxXmlNode( wxXML_ELEMENT_NODE, aName );
322 if( !aDescription.IsEmpty() )
323 node->AddAttribute( wxS(
"description" ), aDescription );
325 aParent->AddChild( node );
329 constexpr std::array<std::pair<const char*, const char*>, 4> sectionInfo = { {
330 {
"Header",
"General Information" },
331 {
"Requirements",
"Board Requirements" },
332 {
"Manufacturing",
"Manufacturing Process" },
333 {
"Assembly",
"Assembly" },
335 std::array<wxXmlNode*, sectionInfo.size()> sections{};
336 wxXmlNode* step =
nullptr;
338 auto sectionNode = [&]( METADATA_SECTION aSection )
340 size_t index =
static_cast<size_t>( aSection );
342 if( !sections[
index] )
344 wxXmlNode* parent = root;
346 if(
index >=
static_cast<size_t>( METADATA_SECTION::MANUFACTURING ) )
350 wxXmlNode* steps = addElement( root, wxS(
"Steps" ), wxS(
"Steps" ) );
351 step = addElement( steps, wxS(
"Step" ), wxString() );
358 sections[
index] = addElement( parent, wxString::FromUTF8( sectionInfo[
index].first ),
359 wxString::FromUTF8( sectionInfo[
index].second ) );
362 return sections[
index];
365 for(
const METADATA_FIELD& field : METADATA_FIELDS )
367 auto value = values.find( field.m_name );
369 if( value == values.end() )
372 wxXmlNode* node = addElement( sectionNode( field.m_section ), wxS(
"node" ), wxString() );
373 node->AddAttribute( wxS(
"name" ), wxString::FromUTF8( field.m_name ) );
374 node->AddAttribute( wxS(
"display" ), wxString::FromUTF8( field.m_display ) );
375 node->AddAttribute( wxS(
"value" ), value->second );
377 if( field.m_distance )
378 node->AddAttribute( wxS(
"units" ), wxString::FromUTF8( format.
m_unitsStr ) );
381 WriteXmlFile( writer,
"metadata.xml", document,
_(
"Failed to generate ODB++ metadata." ) );
404 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.
GetList();
405 std::set<PCB_LAYER_ID> added_layers;
412 for(
int i = 0; i < stackup.
GetCount(); i++ )
416 for(
int sublayer_id = 0; sublayer_id < stackup_item->
GetSublayersCount(); sublayer_id++ )
420 if( ly_name.IsEmpty() )
463 if( added_layers.find( layer ) != added_layers.end() )
467 added_layers.insert( layer );
482 std::map<const BOARD_STACKUP_ITEM*, std::pair<PCB_LAYER_ID, PCB_LAYER_ID>> adjacentCopper;
497 for(
size_t probe =
index; probe-- > 0; )
532 return SECTION::COMPONENTS;
537 return SECTION::DRILL_ROUT;
541 return SECTION::MISC_FAB;
543 switch( aLayer.m_type )
553 default:
return SECTION::DOCUMENTATION;
557 std::set<PCB_LAYER_ID> excluded;
561 if( !
override.m_include )
562 excluded.insert(
override.m_layer );
568 return !format.
Includes( sectionFor( aLayer ) )
575 std::set<wxString> usedNames;
578 usedNames.insert( layer.m_layerName );
589 if( !
override.m_odbName.IsEmpty() )
593 if(
name.IsEmpty() || (
name != layer.m_layerName && usedNames.contains(
name ) ) )
595 m_plugin->Report(
_(
"ODB++ layer name override is empty or collides; using automatic name." ),
600 usedNames.erase( layer.m_layerName );
601 layer.m_layerName =
name;
602 usedNames.insert(
name );
606 if( !
override.m_odbType.IsEmpty() )
608 std::string type =
override.m_odbType.ToStdString();
610 auto it = std::find_if( allowed.begin(), allowed.end(),
613 return ODB::Enum2String( aType ) == type;
616 if( it != allowed.end() )
619 m_plugin->Report(
_(
"Invalid ODB++ layer type override; using automatic type." ),
628 std::map<PCB_LAYER_ID, wxString> namesByLayer;
634 namesByLayer[layer.m_info.m_layer] = layer.m_layerName;
658 if( namesByLayer.contains( start ) && namesByLayer.contains(
end ) )
660 aLayer.
m_span = { namesByLayer.at( start ), namesByLayer.at(
end ) };
664 wxCHECK_MSG( filtered,
false, wxS(
"ODB++ span layer has no matrix row" ) );
672 aLayer.
m_span = { wxEmptyString, wxEmptyString };
676 m_plugin->Report(
_(
"ODB++ partial drill layer omitted because a span layer is excluded." ),
687 std::map<PCB_LAYER_ID, uint32_t> copperIds;
695 copperIds[layer.m_info.m_layer] = layer.m_uid;
711 if(
auto it = copperIds.find( copper ); it != copperIds.end() )
712 layer.
m_ref = it->second;
722 if(
auto it = copperIds.find(
top ); it != copperIds.end() )
725 if(
auto it = copperIds.find( bottom ); it != copperIds.end() )
731 for(
size_t top = i;
top-- > 0; )
740 for(
size_t bottom = i + 1; bottom <
m_matrixLayers.size(); ++bottom )
750 std::vector<ODB_LAYER_NAME>& names =
m_plugin->GetLayerNameList();
756 names.push_back( layer.m_info );
757 names.back().m_name = layer.m_layerName;
847 switch(
m_board->GetLayerType( aLayer ) )
873 std::map<DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
875 std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
878 drill_layers.clear();
881 bool has_pth_layer =
false;
882 bool has_npth_layer =
false;
886 for(
PAD*
pad : fp->Pads() )
894 std::map<DRILL_SPAN, std::unordered_set<BOARD_ITEM*>> seen;
898 auto addOperation = [&](
const DRILL_OPERATION& aOperation,
bool aSlot )
902 bool fromBack = !layer.m_Span.m_IsBackdrill && item->
Type() ==
PCB_VIA_T
904 DRILL_SPAN span( fromBack ? layer.m_Span.BottomLayer() : layer.m_Span.DrillStartLayer(),
905 fromBack ? layer.m_Span.TopLayer() : layer.m_Span.DrillEndLayer(),
906 layer.m_Span.m_IsBackdrill, aOperation.
m_NotPlated );
910 slot_holes[span.
Pair()].push_back( item );
914 if( seen[span].insert( item ).second )
915 drill_layers[span].push_back( item );
920 has_pth_layer |= !op.
m_NotPlated && !layer.m_Span.m_IsBackdrill;
921 addOperation( op,
false );
925 addOperation( op,
true );
931 drill_layers[npthSpan];
937 drill_layers[platedSpan];
940 int backdrillIndex = 1;
942 auto assignName = [&](
const DRILL_SPAN& aSpan )
946 if( aSpan.m_IsBackdrill )
948 name.Printf( wxT(
"drill%d" ), backdrillIndex++ );
952 wxString platedLabel = aSpan.m_IsNonPlatedFile ? wxT(
"non-plated" ) : wxT(
"plated" );
953 name.Printf( wxT(
"drill_%s_%s-%s" ), platedLabel,
m_board->GetLayerName( aSpan.TopLayer() ),
954 m_board->GetLayerName( aSpan.BottomLayer() ) );
960 auto InitDrillMatrix = [&](
const DRILL_SPAN& aSpan )
962 wxString dLayerName = assignName( aSpan );
974 if( aSpan.m_IsBackdrill )
980 for(
const auto& entry : drill_layers )
981 InitDrillMatrix( entry.first );
1012 auto& auxilliary_layers =
m_plugin->GetAuxilliaryLayerItemsMap();
1013 auxilliary_layers.clear();
1031 via->BottomLayer() )]
1038 via->BottomLayer() )]
1072 wxString featureName =
"";
1073 switch( std::get<0>( aLayerPair ) )
1083 wxString dLayerName;
1087 dLayerName = wxString::Format(
"%s_%s-%s", featureName,
1088 m_board->GetLayerName( std::get<1>( aLayerPair ) ),
1089 m_board->GetLayerName( std::get<2>( aLayerPair ) ) );
1093 if(
m_board->IsFrontLayer( std::get<1>( aLayerPair ) ) )
1094 dLayerName = wxString::Format(
"%s_front", featureName );
1095 else if(
m_board->IsBackLayer( std::get<1>( aLayerPair ) ) )
1096 dLayerName = wxString::Format(
"%s_back", featureName );
1107 ? std::get<1>( aLayerPair )
1114 matrix.
m_span.emplace( std::make_pair(
1117 m_board->GetLayerName( std::get<2>( aLayerPair ) ) ) ) );
1123 for(
const auto& [layer_pair, vec] : auxilliary_layers )
1125 InitAuxMatrix( layer_pair );
1132 std::vector<wxString> bases;
1136 bases.push_back( layer.m_layerName );
1167 if( layer.m_addType.has_value() )
1176 if( layer.m_diType.has_value() )
1180 if( !layer.m_dielectricName.IsEmpty() )
1186 if( layer.m_cuBottom )
1187 twriter.
WriteEquationLine(
"CU_BOTTOM",
static_cast<int>( *layer.m_cuBottom ) );
1193 if( layer.m_span.has_value() )
1211 wxString units = wxString::FromUTF8( format.
m_unitsStr.c_str() );
1213 wxXmlDocument document;
1215 auto add = []( wxXmlNode* aParent,
const wxString& aName )
1217 wxXmlNode* node =
new wxXmlNode( wxXML_ELEMENT_NODE, aName );
1220 aParent->AddChild( node );
1225 wxXmlNode* root = add(
nullptr, wxS(
"StackupFile" ) );
1226 document.SetRoot( root );
1227 root->AddAttribute( wxS(
"Version" ), wxS(
"8.1" ) );
1228 root->AddAttribute( wxS(
"DefaultUnits" ), units );
1230 wxXmlNode* eda = add( root, wxS(
"EdaData" ) );
1231 eda->AddAttribute( wxS(
"CompanyName" ),
m_board->GetTitleBlock().GetCompany() );
1232 wxXmlNode* specs = add( eda, wxS(
"Specs" ) );
1233 wxXmlNode* spec = add( specs, wxS(
"Spec" ) );
1234 spec->AddAttribute( wxS(
"SpecName" ), stepName );
1235 wxXmlNode* stackupNode = add( eda, wxS(
"Stackup" ) );
1236 stackupNode->AddAttribute( wxS(
"StackupName" ), stepName );
1238 stackupNode->AddAttribute( wxS(
"Units" ), units );
1239 bool maskThickness =
false;
1247 stackupNode->AddAttribute( wxS(
"WhereMeasured" ), maskThickness ? wxS(
"MASK" ) : wxS(
"METAL" ) );
1248 wxXmlNode*
group = add( stackupNode, wxS(
"Group" ) );
1249 group->AddAttribute( wxS(
"GroupName" ), stepName );
1256 wxXmlNode* layer = add(
group, wxS(
"Layer" ) );
1257 layer->AddAttribute( wxS(
"LayerName" ), row.m_layerName );
1258 layer->AddAttribute( wxS(
"LayerType" ),
1262 layer->AddAttribute( wxS(
"Side" ), wxS(
"INNER" ) );
1263 else if(
m_board->IsFrontLayer( row.m_info.m_layer ) )
1264 layer->AddAttribute( wxS(
"Side" ), wxS(
"TOP" ) );
1265 else if(
m_board->IsBackLayer( row.m_info.m_layer ) )
1266 layer->AddAttribute( wxS(
"Side" ), wxS(
"BOTTOM" ) );
1268 layer->AddAttribute( wxS(
"Side" ), wxS(
"INNER" ) );
1272 if( !row.m_span->first.IsEmpty() )
1273 layer->AddAttribute( wxS(
"MechStartLayerName" ), row.m_span->first );
1275 if( !row.m_span->second.IsEmpty() )
1276 layer->AddAttribute( wxS(
"MechEndLayerName" ), row.m_span->second );
1280 int sublayer = row.m_info.m_sublayer;
1285 wxXmlNode* material = add( spec, wxS(
"Material" ) );
1286 material->AddAttribute( wxS(
"MaterialName" ), row.m_layerName );
1290 wxXmlNode* conductor = add( material, wxS(
"Conductor" ) );
1291 conductor->AddAttribute( wxS(
"ConductorType" ), wxS(
"COPPER" ) );
1292 conductor->AddAttribute( wxS(
"CopperWeight_oz_ft2" ),
1297 wxXmlNode* dielectric = add( material, wxS(
"Dielectric" ) );
1301 dielectric->AddAttribute( wxS(
"DielectricType" ), wxS(
"OTHER" ) );
1302 dielectric->AddAttribute( wxS(
"OtherSubType" ), wxS(
"SOLDER_MASK" ) );
1306 dielectric->AddAttribute( wxS(
"DielectricType" ),
1309 : wxS(
"UNDEFINED" ) );
1312 wxString reference = item->
GetMaterial( sublayer );
1315 dielectric->AddAttribute( wxS(
"MaterialReference" ), reference );
1320 if( hasDk || hasDf )
1322 wxXmlNode* properties = add( dielectric, wxS(
"Properties" ) );
1323 properties->AddAttribute( wxS(
"PropertyName" ), wxS(
"default" ) );
1324 wxXmlNode*
property = add( properties, wxS(
"Property" ) );
1327 if( frequency > 0.0 )
1329 property->AddAttribute( wxS(
"FrequencyVal" ),
ODB::Double2String( format, frequency / 1e9 ) );
1330 property->AddAttribute( wxS(
"Units" ), wxS(
"GHz" ) );
1335 property->AddAttribute( wxS(
"DielectricConstant_Dk" ),
1341 property->AddAttribute( wxS(
"LossTangent_Df" ),
1347 wxXmlNode* thickness = add( material, wxS(
"Default_Thickness" ) );
1349 thickness->AddAttribute( wxS(
"Units" ), units );
1351 wxXmlNode* ref = add( layer, wxS(
"SpecRef" ) );
1352 ref->AddAttribute( wxS(
"MaterialSpecName" ), stepName );
1353 wxXmlNode* named = add( ref, wxS(
"Material" ) );
1354 named->AddAttribute( wxS(
"MaterialName" ), row.m_layerName );
1357 WriteXmlFile( writer,
"stackup.xml", document,
_(
"Failed to generate ODB++ stackup." ) );
1362 const std::map<
int, std::vector<BOARD_ITEM*>>& aMap,
1411 std::vector<BOARD_ITEM*>& vec =
m_layerItems[net->GetNetCode()];
1416 std::stable_sort( vec.begin(), vec.end(),
FabItemLess );
1426 bool hasOutline =
m_board->GetBoardPolygonOutlines( outlines,
false,
nullptr,
true );
1430 m_plugin->Report(
_(
"Board outline is not closed; ODB++ rout layer has no board contour." ),
1435 bool edgePlated =
m_plugin->GetFabStackup().Stackup().m_EdgePlating;
1437 for(
int island = 0; hasOutline && island < outlines.
OutlineCount(); ++island )
1441 for(
int hole = 0; hole < outlines.
HoleCount( island ); ++hole )
1445 for(
const auto& [span, items] :
m_plugin->GetSlotHolesMap() )
1468 std::map<DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
1470 std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
1473 wxCHECK_RET(
m_layer.m_drillSpan.has_value(),
"Drill matrix row has no span" );
1488 if( !isBackdrillLayer )
1493 auto slotIt = slot_holes.find( matchedSpan.
Pair() );
1495 if( slotIt != slot_holes.end() )
1506 if( isNPTHLayer != padIsNPTH )
1510 padIsNPTH ? wxT(
"NON_PLATED" ) : wxT(
"PLATED" ),
1511 std::min(
pad->GetDrillSizeX(),
pad->GetDrillSizeY() ),
1513 ? wxT(
"PRESS_FIT" ) : wxT(
"STANDARD" ) );
1520 auto drillIt = drill_layers.find( matchedSpan );
1522 if( drillIt != drill_layers.end() )
1534 if( isBackdrillLayer )
1536 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), wxT(
"NON_PLATED" ),
1540 else if( isNonPlatedLayer )
1542 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), wxT(
"NON_PLATED" ),
1550 : wxT(
"STANDARD" ) );
1565 if( isNPTHLayer && !padIsNPTH )
1568 if( !isNonPlatedLayer && padIsNPTH )
1573 wxString typeLabel = ( padIsNPTH || isNonPlatedLayer ) ? wxT(
"NON_PLATED" ) : wxT(
"PLATED" );
1574 wxString type2 = isBackdrillLayer ? wxT(
"BLIND" ) : wxT(
"STANDARD" );
1577 type2 = wxT(
"PRESS_FIT" );
1579 m_tools.value().AddDrillTool(
m_plugin->GetFormat(), typeLabel, drillSize, type2 );
1589 auto& auxilliary_layers =
m_plugin->GetAuxilliaryLayerItemsMap();
1591 wxCHECK_RET(
m_layer.m_auxKey.has_value(),
"Auxiliary matrix row has no key" );
1598 auto auxIt = auxilliary_layers.find( *
m_layer.m_auxKey );
1600 if( auxIt == auxilliary_layers.end() )
1623 layer_entity_ptr->InitEntityData();
1656 m_featuresMgr->GenerateFeatureFile( fileproxy.GetStream() );
1664 std::ostream& ost = fileproxy.
GetStream();
1665 ost <<
"UNITS=" <<
m_plugin->GetFormat().m_unitsStr <<
'\n';
1670 ost <<
".drill_layer_direction=bottom2top" << std::endl;
1681 const std::vector<ODB_LAYER_NAME>& layers =
m_plugin->GetLayerNameList();
1682 auto row = std::find_if( layers.begin(), layers.end(),
1685 return aLayer.m_role == ODB_LAYER_ROLE::BOARD_LAYER
1686 && aLayer.m_layer == stop;
1689 if( row != layers.end() )
1690 ost <<
".backdrill_penetrate_stop_layer=" << row->m_name.ToStdString() << std::endl;
1698 int sublayer =
m_layer.m_sublayer;
1714 double epsilonR = stackupItem->
GetEpsilonR( sublayer );
1716 if( epsilonR > 0.0 )
1726 if( lossTangent > 0.0 )
1732 wxString material = stackupItem->
GetMaterial( sublayer );
1734 if( !material.IsEmpty() )
1746 m_tools.value().GenerateFile( fileproxy.GetStream() );
1769 std::array<wxString, 2> componentNames;
1774 componentNames[layer.m_componentSide ==
B_Cu ? 1 : 0] = layer.m_name;
1781 std::shared_ptr<FOOTPRINT> fp_pkg =
m_edaData.GetEdaFootprints().at( j );
1787 bool hasCourtyard = std::any_of( fp->GraphicalItems().begin(), fp->GraphicalItems().end(),
1790 return item->IsOnLayer( F_CrtYd ) || item->IsOnLayer( B_CrtYd );
1798 if( writeComponents )
1800 auto iter =
m_layerEntityMap.find( componentNames[fp->IsFlipped() ? 1 : 0] );
1803 comp = &iter->second->InitComponentData( fp, eda_pkg );
1806 for(
int i = 0; i < (int) fp->Pads().size(); ++i )
1808 PAD*
pad = fp->Pads()[i];
1816 m_plugin->GetPadSubnetMap().emplace(
pad, &subnet );
1823 auto& toep =
comp->m_toeprints.emplace_back( pin_ref );
1825 toep.m_net_num = eda_net.
m_index;
1826 toep.m_subnet_num = subnet.
m_index;
1831 (
ANGLE_360 -
pad->GetOrientation() ).Normalize().AsDegrees() );
1833 if(
pad->IsFlipped() )
1834 toep.m_mirror = wxT(
"M" );
1836 toep.m_mirror = wxT(
"N" );
1850 m_plugin->GetViaTraceSubnetMap().emplace( track, subnet );
1866 m_plugin->GetPlaneSubnetMap().emplace( std::piecewise_construct,
1867 std::forward_as_tuple( layer, zone ),
1868 std::forward_as_tuple( &subnet ) );
1901 int thickness =
m_plugin->GetFabStackup().Thickness();
1903 attrlist.GetStream() <<
"UNITS=" << format.
m_unitsStr <<
'\n';
1904 attrlist.GetStream() <<
".board_thickness="
1909 if( !selected.IsEmpty() )
1911 attrlist.GetStream() <<
".current_variant=" << selected.ToStdString() <<
'\n';
1925 if( !
m_board->GetBoardPolygonOutlines( board_outline,
true ) )
1926 wxLogTrace(
traceOdbppIo,
"Failed to get board outline" );
1930 double largestArea = count > 0 ? board_outline.
Outline( 0 ).
Area() : 0.0;
1932 for(
int ii = 1; ii < count; ++ii )
1936 if( area > largestArea )
1943 if( !
m_profile->AddContour( board_outline, largest ) )
1944 wxLogTrace(
traceOdbppIo,
"Failed to add polygon to profile" );
1946 m_profile->GenerateProfileFeatures( fileproxy.GetStream() );
1958 {
"X_ORIGIN",
"0" },
1959 {
"Y_ORIGIN",
"0" },
1960 {
"TOP_ACTIVE",
"0" },
1961 {
"BOTTOM_ACTIVE",
"0" },
1962 {
"RIGHT_ACTIVE",
"0" },
1963 {
"LEFT_ACTIVE",
"0" },
1964 {
"AFFECTING_BOM",
"" },
1965 {
"AFFECTING_BOM_CHANGED",
"0" },
1970 for(
const auto& [key, value] :
m_stephdr )
1985 layerEntity->GenerateFiles( writer );
1994 std::set<int> m_nets;
1996 size_t m_featureIndex;
2000std::vector<SHORT_SHAPE> CollectNetTieShorts(
BOARD* aBoard,
PCB_IO_ODBPP* aPlugin )
2002 std::vector<SHORT_SHAPE> shortShapes;
2006 const auto* shape =
static_cast<const PCB_SHAPE*
>( shapeLayer.first );
2010 auto copper = shape->GetEffectiveShape( layer );
2011 std::set<int> netCodes;
2013 bool invalidGroup =
false;
2018 if(
pad->GetNetCode() <= 0 || !
pad->GetLayerSet()[layer]
2019 || !
pad->GetEffectiveShape( layer )->Collide( copper.get() ) )
2024 auto padGroup = groups.find(
pad->GetNumber() );
2025 int currentGroup = padGroup == groups.end() ? -1 : padGroup->second;
2027 if( currentGroup < 0 || (
group >= 0 &&
group != currentGroup ) )
2028 invalidGroup =
true;
2030 group = currentGroup;
2031 netCodes.insert(
pad->GetNetCode() );
2036 wxLogTrace(
traceOdbppIo, wxT(
"Net tie shape touches pads outside one group" ) );
2040 if( netCodes.size() >= 2 )
2041 shortShapes.push_back( { std::move( netCodes ), feature.first, feature.second } );
2049 bool hasCopperShape = std::any_of( footprint->
GraphicalItems().begin(),
2053 return item->Type() == PCB_SHAPE_T
2054 && ( item->GetLayerSet() & LSET::AllCuMask() ).any();
2057 if( !hasCopperShape )
2058 wxLogTrace(
traceOdbppIo, wxT(
"Net tie has no copper shape for shortf" ) );
2061 std::sort( shortShapes.begin(), shortShapes.end(), [](
const SHORT_SHAPE&
left,
const SHORT_SHAPE&
right )
2062 { return std::tie( left.m_layer, left.m_featureIndex )
2063 < std::tie( right.m_layer, right.m_featureIndex ); } );
2072 std::vector<SHORT_SHAPE> shortShapes;
2074 if(
m_plugin->GetFormat().m_writeEdaNets )
2077 if( !shortShapes.empty() )
2081 for(
const SHORT_SHAPE& shape : shortShapes )
2082 m_edaData.AddShort( shape.m_nets, shape.m_layer, shape.m_featureIndex,
m_plugin->NewUid() );
2113 catch(
const std::exception& e )
2115 std::cerr << e.what() << std::endl;
2127 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