mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
Recommendation is to avoid using the year nomenclature as this information is already encoded in the git repo. Avoids needing to repeatly update. Also updates AUTHORS.txt from current repo with contributor names
977 lines
30 KiB
C++
977 lines
30 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
* Author: SYSUEric <jzzhuang666@gmail.com>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include <base_units.h>
|
|
#include <board_stackup_manager/stackup_predefined_prms.h>
|
|
#include <build_version.h>
|
|
#include <callback_gal.h>
|
|
#include <connectivity/connectivity_data.h>
|
|
#include <connectivity/connectivity_algo.h>
|
|
#include <convert_basic_shapes_to_polygon.h>
|
|
#include <font/font.h>
|
|
#include <footprint.h>
|
|
#include <hash_eda.h>
|
|
#include <pad.h>
|
|
#include <pcb_dimension.h>
|
|
#include <pcb_shape.h>
|
|
#include <pcb_text.h>
|
|
#include <pcb_textbox.h>
|
|
#include <pcb_track.h>
|
|
#include <pcbnew_settings.h>
|
|
#include <board_design_settings.h>
|
|
#include <pgm_base.h>
|
|
#include <progress_reporter.h>
|
|
#include <settings/settings_manager.h>
|
|
#include <wx_fstream_progress.h>
|
|
|
|
#include <geometry/shape_circle.h>
|
|
#include <geometry/shape_line_chain.h>
|
|
#include <geometry/shape_poly_set.h>
|
|
#include <geometry/shape_segment.h>
|
|
|
|
#include <wx/log.h>
|
|
#include <wx/numformatter.h>
|
|
#include <wx/mstream.h>
|
|
|
|
#include "odb_attribute.h"
|
|
#include "odb_entity.h"
|
|
#include "odb_defines.h"
|
|
#include "odb_feature.h"
|
|
#include "odb_util.h"
|
|
#include "pcb_io_odbpp.h"
|
|
|
|
|
|
bool ODB_ENTITY_BASE::CreateDirectoryTree( ODB_TREE_WRITER& writer )
|
|
{
|
|
try
|
|
{
|
|
writer.CreateEntityDirectory( writer.GetRootPath(), GetEntityName() );
|
|
return true;
|
|
}
|
|
catch( const std::exception& e )
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
ODB_MISC_ENTITY::ODB_MISC_ENTITY()
|
|
{
|
|
m_info = { { wxS( ODB_JOB_NAME ), wxS( "job" ) },
|
|
{ wxS( ODB_UNITS ), PCB_IO_ODBPP::m_unitsStr },
|
|
{ wxS( "ODB_VERSION_MAJOR" ), wxS( "8" ) },
|
|
{ wxS( "ODB_VERSION_MINOR" ), wxS( "1" ) },
|
|
{ wxS( "ODB_SOURCE" ), wxS( "KiCad EDA" ) },
|
|
{ wxS( "CREATION_DATE" ), wxDateTime::Now().FormatISOCombined() },
|
|
{ wxS( "SAVE_DATE" ), wxDateTime::Now().FormatISOCombined() },
|
|
{ wxS( "SAVE_APP" ), wxString::Format( wxS( "KiCad EDA %s" ), GetBuildVersion() ) } };
|
|
}
|
|
|
|
|
|
void ODB_MISC_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "info" );
|
|
|
|
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
|
|
|
|
for( auto& info : m_info )
|
|
{
|
|
twriter.WriteEquationLine( info.first, info.second );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::AddStep( const wxString& aStepName )
|
|
{
|
|
m_matrixSteps.emplace( aStepName.Upper(), m_col++ );
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::InitEntityData()
|
|
{
|
|
AddStep( "PCB" );
|
|
|
|
InitMatrixLayerData();
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::InitMatrixLayerData()
|
|
{
|
|
BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
|
|
BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
|
|
stackup.SynchronizeWithBoard( &dsnSettings );
|
|
|
|
std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
|
|
std::set<PCB_LAYER_ID> added_layers;
|
|
|
|
AddCOMPMatrixLayer( F_Cu );
|
|
|
|
for( int i = 0; i < stackup.GetCount(); i++ )
|
|
{
|
|
BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
|
|
|
|
for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
|
|
{
|
|
wxString ly_name = stackup_item->GetLayerName();
|
|
|
|
if( ly_name.IsEmpty() )
|
|
{
|
|
if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
|
|
ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
|
|
|
|
if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
|
|
ly_name = wxString::Format( "DIELECTRIC_%d",
|
|
stackup_item->GetDielectricLayerId() );
|
|
}
|
|
|
|
MATRIX_LAYER matrix( m_row++, ly_name );
|
|
|
|
if( stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
|
|
{
|
|
if( stackup_item->GetTypeName() == KEY_CORE )
|
|
matrix.m_diType.emplace( ODB_DIELECTRIC_TYPE::CORE );
|
|
else
|
|
matrix.m_diType.emplace( ODB_DIELECTRIC_TYPE::PREPREG );
|
|
|
|
matrix.m_type = ODB_TYPE::DIELECTRIC;
|
|
matrix.m_context = ODB_CONTEXT::BOARD;
|
|
matrix.m_polarity = ODB_POLARITY::POSITIVE;
|
|
m_matrixLayers.push_back( matrix );
|
|
m_plugin->GetLayerNameList().emplace_back(
|
|
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
added_layers.insert( stackup_item->GetBrdLayerId() );
|
|
AddMatrixLayerField( matrix, stackup_item->GetBrdLayerId() );
|
|
}
|
|
}
|
|
}
|
|
|
|
LSEQ layer_seq = m_board->GetEnabledLayers().Seq();
|
|
|
|
for( PCB_LAYER_ID layer : layer_seq )
|
|
{
|
|
if( added_layers.find( layer ) != added_layers.end() )
|
|
continue;
|
|
|
|
MATRIX_LAYER matrix( m_row++, m_board->GetLayerName( layer ) );
|
|
added_layers.insert( layer );
|
|
AddMatrixLayerField( matrix, layer );
|
|
}
|
|
|
|
AddDrillMatrixLayer();
|
|
|
|
AddCOMPMatrixLayer( B_Cu );
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::AddMatrixLayerField( MATRIX_LAYER& aMLayer, PCB_LAYER_ID aLayer )
|
|
{
|
|
aMLayer.m_polarity = ODB_POLARITY::POSITIVE;
|
|
aMLayer.m_context = ODB_CONTEXT::BOARD;
|
|
switch( aLayer )
|
|
{
|
|
case F_Paste:
|
|
case B_Paste: aMLayer.m_type = ODB_TYPE::SOLDER_PASTE; break;
|
|
case F_SilkS:
|
|
case B_SilkS: aMLayer.m_type = ODB_TYPE::SILK_SCREEN; break;
|
|
case F_Mask:
|
|
case B_Mask: aMLayer.m_type = ODB_TYPE::SOLDER_MASK; break;
|
|
case B_CrtYd:
|
|
case F_CrtYd:
|
|
case Edge_Cuts:
|
|
case B_Fab:
|
|
case F_Fab:
|
|
case F_Adhes:
|
|
case B_Adhes:
|
|
case Dwgs_User:
|
|
case Cmts_User:
|
|
case Eco1_User:
|
|
case Eco2_User:
|
|
case Margin:
|
|
case User_1:
|
|
case User_2:
|
|
case User_3:
|
|
case User_4:
|
|
case User_5:
|
|
case User_6:
|
|
case User_7:
|
|
case User_8:
|
|
case User_9:
|
|
aMLayer.m_context = ODB_CONTEXT::MISC;
|
|
aMLayer.m_type = ODB_TYPE::DOCUMENT;
|
|
break;
|
|
|
|
default:
|
|
if( IsCopperLayer( aLayer ) )
|
|
{
|
|
aMLayer.m_type = ODB_TYPE::SIGNAL;
|
|
}
|
|
else
|
|
{
|
|
// Do not handle other layers :
|
|
aMLayer.m_type = ODB_TYPE::UNDEFINED;
|
|
m_row--;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if( aMLayer.m_type != ODB_TYPE::UNDEFINED )
|
|
{
|
|
m_matrixLayers.push_back( aMLayer );
|
|
m_plugin->GetLayerNameList().emplace_back( std::make_pair( aLayer, aMLayer.m_layerName ) );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::AddDrillMatrixLayer()
|
|
{
|
|
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
|
|
m_plugin->GetDrillLayerItemsMap();
|
|
|
|
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
|
|
m_plugin->GetSlotHolesMap();
|
|
|
|
bool has_pth_layer = false;
|
|
bool has_npth_layer = false;
|
|
|
|
for( BOARD_ITEM* item : m_board->Tracks() )
|
|
{
|
|
if( item->Type() == PCB_VIA_T )
|
|
{
|
|
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
|
drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
|
|
}
|
|
}
|
|
|
|
for( FOOTPRINT* fp : m_board->Footprints() )
|
|
{
|
|
// std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( it_fp->Clone() ) );
|
|
|
|
if( fp->IsFlipped() )
|
|
{
|
|
m_hasBotComp = true;
|
|
}
|
|
|
|
for( PAD* pad : fp->Pads() )
|
|
{
|
|
if( !has_pth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
|
has_pth_layer = true;
|
|
if( !has_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
|
has_npth_layer = true;
|
|
|
|
if( pad->HasHole() && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
|
|
slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
|
|
else if( pad->HasHole() )
|
|
drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
|
|
}
|
|
|
|
// m_plugin->GetLoadedFootprintList().push_back( std::move( fp ) );
|
|
}
|
|
|
|
auto InitDrillMatrix =
|
|
[&]( const wxString& aHasPlated, std::pair<PCB_LAYER_ID, PCB_LAYER_ID> aLayerPair )
|
|
{
|
|
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", aHasPlated,
|
|
m_board->GetLayerName( aLayerPair.first ),
|
|
m_board->GetLayerName( aLayerPair.second ) );
|
|
MATRIX_LAYER matrix( m_row++, dLayerName );
|
|
|
|
matrix.m_type = ODB_TYPE::DRILL;
|
|
matrix.m_context = ODB_CONTEXT::BOARD;
|
|
matrix.m_polarity = ODB_POLARITY::POSITIVE;
|
|
matrix.m_span.emplace( std::make_pair(
|
|
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.first ) ),
|
|
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.second ) ) ) );
|
|
m_matrixLayers.push_back( matrix );
|
|
m_plugin->GetLayerNameList().emplace_back(
|
|
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
|
|
};
|
|
|
|
if( drill_layers.find( std::make_pair( F_Cu, B_Cu ) ) != drill_layers.end()
|
|
|| !slot_holes.empty() )
|
|
{
|
|
// for pad has hole
|
|
if( has_pth_layer )
|
|
InitDrillMatrix( "plated", std::make_pair( F_Cu, B_Cu ) );
|
|
if( has_npth_layer )
|
|
InitDrillMatrix( "non-plated", std::make_pair( F_Cu, B_Cu ) );
|
|
}
|
|
|
|
for( const auto& [layer_pair, vec] : drill_layers )
|
|
{
|
|
if( layer_pair != std::make_pair( F_Cu, B_Cu ) ) // pad has initialized above
|
|
InitDrillMatrix( "plated", layer_pair ); // for via
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::AddCOMPMatrixLayer( PCB_LAYER_ID aCompSide )
|
|
{
|
|
MATRIX_LAYER matrix( m_row++, "COMP_+_TOP" );
|
|
matrix.m_type = ODB_TYPE::COMPONENT;
|
|
matrix.m_context = ODB_CONTEXT::BOARD;
|
|
|
|
if( aCompSide == F_Cu )
|
|
{
|
|
m_matrixLayers.push_back( matrix );
|
|
m_plugin->GetLayerNameList().emplace_back(
|
|
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
|
|
}
|
|
|
|
if( aCompSide == B_Cu && m_hasBotComp )
|
|
{
|
|
matrix.m_layerName = ODB::GenLegalEntityName( "COMP_+_BOT" );
|
|
m_matrixLayers.push_back( matrix );
|
|
m_plugin->GetLayerNameList().emplace_back(
|
|
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_MATRIX_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "matrix" );
|
|
|
|
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
|
|
|
|
for( const auto& [step_name, column] : m_matrixSteps )
|
|
{
|
|
const auto array_proxy = twriter.MakeArrayProxy( "STEP" );
|
|
twriter.WriteEquationLine( "COL", column );
|
|
twriter.WriteEquationLine( "NAME", step_name );
|
|
}
|
|
|
|
for( const MATRIX_LAYER& layer : m_matrixLayers )
|
|
{
|
|
const auto array_proxy = twriter.MakeArrayProxy( "LAYER" );
|
|
twriter.WriteEquationLine( "ROW", layer.m_rowNumber );
|
|
twriter.write_line_enum( "CONTEXT", layer.m_context );
|
|
twriter.write_line_enum( "TYPE", layer.m_type );
|
|
|
|
if( layer.m_addType.has_value() )
|
|
{
|
|
twriter.write_line_enum( "ADD_TYPE", layer.m_addType.value() );
|
|
}
|
|
|
|
twriter.WriteEquationLine( "NAME", layer.m_layerName.Upper() );
|
|
twriter.WriteEquationLine( "OLD_NAME", wxEmptyString );
|
|
twriter.write_line_enum( "POLARITY", layer.m_polarity );
|
|
|
|
if( layer.m_diType.has_value() )
|
|
{
|
|
twriter.write_line_enum( "DIELECTRIC_TYPE", layer.m_diType.value() );
|
|
// twriter.WriteEquationLine( "DIELECTRIC_NAME", wxEmptyString );
|
|
|
|
// Can be used with DIELECTRIC_TYPE=CORE
|
|
// twriter.WriteEquationLine( "CU_TOP", wxEmptyString );
|
|
// twriter.WriteEquationLine( "CU_BOTTOM", wxEmptyString );
|
|
}
|
|
|
|
// Only applies to: soldermask, silkscreen, solderpaste and specifies the relevant cu layer
|
|
// twriter.WriteEquationLine( "REF", wxEmptyString );
|
|
|
|
if( layer.m_span.has_value() )
|
|
{
|
|
twriter.WriteEquationLine( "START_NAME", layer.m_span->first.Upper() );
|
|
twriter.WriteEquationLine( "END_NAME", layer.m_span->second.Upper() );
|
|
}
|
|
|
|
twriter.WriteEquationLine( "COLOR", "0" );
|
|
}
|
|
}
|
|
|
|
|
|
ODB_LAYER_ENTITY::ODB_LAYER_ENTITY( BOARD* aBoard, PCB_IO_ODBPP* aPlugin,
|
|
std::map<int, std::vector<BOARD_ITEM*>>& aMap,
|
|
const PCB_LAYER_ID& aLayerID, const wxString& aLayerName ) :
|
|
ODB_ENTITY_BASE( aBoard, aPlugin ), m_layerItems( aMap ), m_layerID( aLayerID ),
|
|
m_matrixLayerName( aLayerName )
|
|
{
|
|
m_featuresMgr = std::make_unique<FEATURES_MANAGER>( aBoard, aPlugin, aLayerName );
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::InitEntityData()
|
|
{
|
|
if( m_matrixLayerName.Contains( "drill" ) )
|
|
{
|
|
InitDrillData();
|
|
InitFeatureData();
|
|
return;
|
|
}
|
|
|
|
if( m_layerID != PCB_LAYER_ID::UNDEFINED_LAYER )
|
|
{
|
|
InitFeatureData();
|
|
}
|
|
}
|
|
|
|
void ODB_LAYER_ENTITY::InitFeatureData()
|
|
{
|
|
if( m_layerItems.empty() )
|
|
return;
|
|
|
|
const NETINFO_LIST& nets = m_board->GetNetInfo();
|
|
|
|
for( const NETINFO_ITEM* net : nets )
|
|
{
|
|
std::vector<BOARD_ITEM*>& vec = m_layerItems[net->GetNetCode()];
|
|
|
|
std::stable_sort( vec.begin(), vec.end(),
|
|
[]( BOARD_ITEM* a, BOARD_ITEM* b )
|
|
{
|
|
if( a->GetParentFootprint() == b->GetParentFootprint() )
|
|
return a->Type() < b->Type();
|
|
|
|
return a->GetParentFootprint() < b->GetParentFootprint();
|
|
} );
|
|
|
|
if( vec.empty() )
|
|
continue;
|
|
|
|
m_featuresMgr->InitFeatureList( m_layerID, vec );
|
|
}
|
|
}
|
|
|
|
|
|
ODB_COMPONENT& ODB_LAYER_ENTITY::InitComponentData( const FOOTPRINT* aFp,
|
|
const EDA_DATA::PACKAGE& aPkg )
|
|
{
|
|
if( m_matrixLayerName == "COMP_+_BOT" )
|
|
{
|
|
if( !m_compBot.has_value() )
|
|
{
|
|
m_compBot.emplace();
|
|
}
|
|
return m_compBot.value().AddComponent( aFp, aPkg );
|
|
}
|
|
else
|
|
{
|
|
if( !m_compTop.has_value() )
|
|
{
|
|
m_compTop.emplace();
|
|
}
|
|
|
|
return m_compTop.value().AddComponent( aFp, aPkg );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::InitDrillData()
|
|
{
|
|
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
|
|
m_plugin->GetDrillLayerItemsMap();
|
|
|
|
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
|
|
m_plugin->GetSlotHolesMap();
|
|
|
|
if( !m_layerItems.empty() )
|
|
{
|
|
m_layerItems.clear();
|
|
}
|
|
|
|
m_tools.emplace( PCB_IO_ODBPP::m_unitsStr );
|
|
|
|
bool is_npth_layer = false;
|
|
wxString plated_name = "plated";
|
|
|
|
if( m_matrixLayerName.Contains( "non-plated" ) )
|
|
{
|
|
is_npth_layer = true;
|
|
plated_name = "non-plated";
|
|
}
|
|
|
|
|
|
for( const auto& [layer_pair, vec] : slot_holes )
|
|
{
|
|
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
|
|
m_board->GetLayerName( layer_pair.first ),
|
|
m_board->GetLayerName( layer_pair.second ) );
|
|
|
|
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
|
{
|
|
for( BOARD_ITEM* item : vec )
|
|
{
|
|
if( item->Type() == PCB_PAD_T )
|
|
{
|
|
PAD* pad = static_cast<PAD*>( item );
|
|
|
|
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
|
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
m_tools.value().AddDrillTools(
|
|
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
|
|
ODB::SymDouble2String(
|
|
std::min( pad->GetDrillSizeX(), pad->GetDrillSizeY() ) ) );
|
|
|
|
// for drill features
|
|
m_layerItems[pad->GetNetCode()].push_back( item );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
for( const auto& [layer_pair, vec] : drill_layers )
|
|
{
|
|
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
|
|
m_board->GetLayerName( layer_pair.first ),
|
|
m_board->GetLayerName( layer_pair.second ) );
|
|
|
|
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
|
{
|
|
for( BOARD_ITEM* item : vec )
|
|
{
|
|
if( item->Type() == PCB_VIA_T && !is_npth_layer )
|
|
{
|
|
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
|
|
|
m_tools.value().AddDrillTools( "VIA",
|
|
ODB::SymDouble2String( via->GetDrillValue() ) );
|
|
|
|
// for drill features
|
|
m_layerItems[via->GetNetCode()].push_back( item );
|
|
}
|
|
else if( item->Type() == PCB_PAD_T )
|
|
{
|
|
PAD* pad = static_cast<PAD*>( item );
|
|
|
|
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
|
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
m_tools.value().AddDrillTools(
|
|
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
|
|
ODB::SymDouble2String( pad->GetDrillSizeX() ) );
|
|
|
|
// for drill features
|
|
m_layerItems[pad->GetNetCode()].push_back( item );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::InitEntityData()
|
|
{
|
|
MakeLayerEntity();
|
|
|
|
InitEdaData();
|
|
|
|
// Init Layer Entity Data
|
|
for( const auto& [layerName, layer_entity_ptr] : m_layerEntityMap )
|
|
{
|
|
layer_entity_ptr->InitEntityData();
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
GenAttrList( writer );
|
|
|
|
GenFeatures( writer );
|
|
|
|
if( m_compTop.has_value() || m_compBot.has_value() )
|
|
{
|
|
GenComponents( writer );
|
|
}
|
|
|
|
if( m_tools.has_value() )
|
|
{
|
|
GenTools( writer );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::GenComponents( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "components" );
|
|
|
|
if( m_compTop.has_value() )
|
|
{
|
|
m_compTop->Write( fileproxy.GetStream() );
|
|
}
|
|
else if( m_compBot.has_value() )
|
|
{
|
|
m_compBot->Write( fileproxy.GetStream() );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::GenFeatures( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "features" );
|
|
|
|
m_featuresMgr->GenerateFeatureFile( fileproxy.GetStream() );
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::GenAttrList( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "attrlist" );
|
|
}
|
|
|
|
|
|
void ODB_LAYER_ENTITY::GenTools( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "tools" );
|
|
|
|
m_tools.value().GenerateFile( fileproxy.GetStream() );
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::InitEdaData()
|
|
{
|
|
//InitPackage
|
|
for( const FOOTPRINT* fp : m_board->Footprints() )
|
|
{
|
|
m_edaData.AddPackage( fp );
|
|
}
|
|
|
|
// for NET
|
|
const NETINFO_LIST& nets = m_board->GetNetInfo();
|
|
|
|
for( const NETINFO_ITEM* net : nets )
|
|
{
|
|
m_edaData.AddNET( net );
|
|
}
|
|
|
|
// for CMP
|
|
size_t j = 0;
|
|
|
|
for( const FOOTPRINT* fp : m_board->Footprints() )
|
|
{
|
|
wxString compName = ODB::GenLegalEntityName( "COMP_+_TOP" );
|
|
if( fp->IsFlipped() )
|
|
compName = ODB::GenLegalEntityName( "COMP_+_BOT" );
|
|
|
|
auto iter = m_layerEntityMap.find( compName );
|
|
|
|
if( iter == m_layerEntityMap.end() )
|
|
{
|
|
wxLogError( _( "Failed to add component data" ) );
|
|
return;
|
|
}
|
|
|
|
// ODBPP only need unique PACKAGE in PKG record in eda/data file.
|
|
// the PKG index can repeat to be ref in CMP record in component file.
|
|
std::shared_ptr<FOOTPRINT> fp_pkg = m_edaData.GetEdaFootprints().at( j );
|
|
++j;
|
|
|
|
const EDA_DATA::PACKAGE& eda_pkg =
|
|
m_edaData.GetPackage( hash_fp_item( fp_pkg.get(), HASH_POS | REL_COORD ) );
|
|
|
|
ODB_COMPONENT& comp = iter->second->InitComponentData( fp, eda_pkg );
|
|
|
|
for( int i = 0; i < fp->Pads().size(); ++i )
|
|
{
|
|
PAD* pad = fp->Pads()[i];
|
|
auto& eda_net = m_edaData.GetNet( pad->GetNetCode() );
|
|
|
|
auto& subnet = eda_net.AddSubnet<EDA_DATA::SUB_NET_TOEPRINT>(
|
|
&m_edaData,
|
|
fp->IsFlipped() ? EDA_DATA::SUB_NET_TOEPRINT::SIDE::BOTTOM
|
|
: EDA_DATA::SUB_NET_TOEPRINT::SIDE::TOP,
|
|
comp.m_index, comp.m_toeprints.size() );
|
|
|
|
m_plugin->GetPadSubnetMap().emplace( pad, &subnet );
|
|
|
|
const std::shared_ptr<EDA_DATA::PIN> pin = eda_pkg.GetEdaPkgPin( i );
|
|
const EDA_DATA::PIN& pin_ref = *pin;
|
|
auto& toep = comp.m_toeprints.emplace_back( pin_ref );
|
|
|
|
toep.m_net_num = eda_net.m_index;
|
|
toep.m_subnet_num = subnet.m_index;
|
|
|
|
toep.m_center = ODB::AddXY( pad->GetPosition() );
|
|
|
|
toep.m_rot = ODB::Double2String(
|
|
( ANGLE_360 - pad->GetOrientation() ).Normalize().AsDegrees() );
|
|
|
|
if( pad->IsFlipped() )
|
|
toep.m_mirror = wxT( "M" );
|
|
else
|
|
toep.m_mirror = wxT( "N" );
|
|
}
|
|
}
|
|
|
|
for( PCB_TRACK* track : m_board->Tracks() )
|
|
{
|
|
auto& eda_net = m_edaData.GetNet( track->GetNetCode() );
|
|
EDA_DATA::SUB_NET* subnet = nullptr;
|
|
|
|
if( track->Type() == PCB_VIA_T )
|
|
subnet = &( eda_net.AddSubnet<EDA_DATA::SUB_NET_VIA>( &m_edaData ) );
|
|
else
|
|
subnet = &( eda_net.AddSubnet<EDA_DATA::SUB_NET_TRACE>( &m_edaData ) );
|
|
|
|
m_plugin->GetViaTraceSubnetMap().emplace( track, subnet );
|
|
}
|
|
|
|
for( ZONE* zone : m_board->Zones() )
|
|
{
|
|
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
|
{
|
|
auto& eda_net = m_edaData.GetNet( zone->GetNetCode() );
|
|
auto& subnet = eda_net.AddSubnet<EDA_DATA::SUB_NET_PLANE>(
|
|
&m_edaData, EDA_DATA::SUB_NET_PLANE::FILL_TYPE::SOLID,
|
|
EDA_DATA::SUB_NET_PLANE::CUTOUT_TYPE::EXACT, 0 );
|
|
m_plugin->GetPlaneSubnetMap().emplace( std::piecewise_construct,
|
|
std::forward_as_tuple( layer, zone ),
|
|
std::forward_as_tuple( &subnet ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
wxString step_root = writer.GetCurrentPath();
|
|
|
|
writer.CreateEntityDirectory( step_root, "layers" );
|
|
GenerateLayerFiles( writer );
|
|
|
|
writer.CreateEntityDirectory( step_root, "eda" );
|
|
GenerateEdaFiles( writer );
|
|
|
|
writer.CreateEntityDirectory( step_root, "netlists/cadnet" );
|
|
GenerateNetlistsFiles( writer );
|
|
|
|
writer.SetCurrentPath( step_root );
|
|
GenerateProfileFile( writer );
|
|
|
|
GenerateStepHeaderFile( writer );
|
|
|
|
//TODO: system attributes
|
|
// GenerateAttrListFile( writer );
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateProfileFile( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "profile" );
|
|
|
|
m_profile = std::make_unique<FEATURES_MANAGER>( m_board, m_plugin, wxEmptyString );
|
|
|
|
SHAPE_POLY_SET board_outline;
|
|
|
|
if( !m_board->GetBoardPolygonOutlines( board_outline ) )
|
|
{
|
|
wxLogError( "Failed to get board outline" );
|
|
}
|
|
|
|
if( !m_profile->AddContour( board_outline, 0 ) )
|
|
{
|
|
wxLogError( "Failed to add polygon to profile" );
|
|
}
|
|
|
|
m_profile->GenerateProfileFeatures( fileproxy.GetStream() );
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateStepHeaderFile( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "stephdr" );
|
|
|
|
m_stephdr = {
|
|
{ ODB_UNITS, PCB_IO_ODBPP::m_unitsStr },
|
|
{ "X_DATUM", "0" },
|
|
{ "Y_DATUM", "0" },
|
|
{ "X_ORIGIN", "0" },
|
|
{ "Y_ORIGIN", "0" },
|
|
{ "TOP_ACTIVE", "0" },
|
|
{ "BOTTOM_ACTIVE", "0" },
|
|
{ "RIGHT_ACTIVE", "0" },
|
|
{ "LEFT_ACTIVE", "0" },
|
|
{ "AFFECTING_BOM", "" },
|
|
{ "AFFECTING_BOM_CHANGED", "0" },
|
|
};
|
|
|
|
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
|
|
|
|
for( const auto& [key, value] : m_stephdr )
|
|
{
|
|
twriter.WriteEquationLine( key, value );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateLayerFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
wxString layers_root = writer.GetCurrentPath();
|
|
|
|
for( auto& [layerName, layerEntity] : m_layerEntityMap )
|
|
{
|
|
writer.CreateEntityDirectory( layers_root, layerName );
|
|
|
|
layerEntity->GenerateFiles( writer );
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateEdaFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "data" );
|
|
|
|
m_edaData.Write( fileproxy.GetStream() );
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::GenerateNetlistsFiles( ODB_TREE_WRITER& writer )
|
|
{
|
|
auto fileproxy = writer.CreateFileProxy( "netlist" );
|
|
|
|
m_netlist.Write( fileproxy.GetStream() );
|
|
}
|
|
|
|
|
|
bool ODB_STEP_ENTITY::CreateDirectoryTree( ODB_TREE_WRITER& writer )
|
|
{
|
|
try
|
|
{
|
|
writer.CreateEntityDirectory( writer.GetRootPath(), "steps" );
|
|
writer.CreateEntityDirectory( writer.GetCurrentPath(), GetEntityName() );
|
|
return true;
|
|
}
|
|
catch( const std::exception& e )
|
|
{
|
|
std::cerr << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void ODB_STEP_ENTITY::MakeLayerEntity()
|
|
{
|
|
LSEQ layers = m_board->GetEnabledLayers().Seq();
|
|
const NETINFO_LIST& nets = m_board->GetNetInfo();
|
|
|
|
// To avoid the overhead of repeatedly cycling through the layers and nets,
|
|
// we pre-sort the board items into a map of layer -> net -> items
|
|
std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>>& elements =
|
|
m_plugin->GetLayerElementsMap();
|
|
|
|
std::for_each( m_board->Tracks().begin(), m_board->Tracks().end(),
|
|
[&layers, &elements]( PCB_TRACK* aTrack )
|
|
{
|
|
if( aTrack->Type() == PCB_VIA_T )
|
|
{
|
|
PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
|
|
|
|
for( PCB_LAYER_ID layer : layers )
|
|
{
|
|
if( via->FlashLayer( layer ) )
|
|
elements[layer][via->GetNetCode()].push_back( via );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elements[aTrack->GetLayer()][aTrack->GetNetCode()].push_back( aTrack );
|
|
}
|
|
} );
|
|
|
|
std::for_each( m_board->Zones().begin(), m_board->Zones().end(),
|
|
[&elements]( ZONE* zone )
|
|
{
|
|
LSEQ zone_layers = zone->GetLayerSet().Seq();
|
|
|
|
for( PCB_LAYER_ID layer : zone_layers )
|
|
{
|
|
elements[layer][zone->GetNetCode()].push_back( zone );
|
|
}
|
|
} );
|
|
|
|
for( BOARD_ITEM* item : m_board->Drawings() )
|
|
{
|
|
if( BOARD_CONNECTED_ITEM* conn_it = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
|
|
elements[conn_it->GetLayer()][conn_it->GetNetCode()].push_back( conn_it );
|
|
else
|
|
elements[item->GetLayer()][0].push_back( item );
|
|
}
|
|
|
|
for( FOOTPRINT* fp : m_board->Footprints() )
|
|
{
|
|
for( PCB_FIELD* field : fp->GetFields() )
|
|
elements[field->GetLayer()][0].push_back( field );
|
|
|
|
for( BOARD_ITEM* item : fp->GraphicalItems() )
|
|
elements[item->GetLayer()][0].push_back( item );
|
|
|
|
for( PAD* pad : fp->Pads() )
|
|
{
|
|
LSEQ pad_layers = pad->GetLayerSet().Seq();
|
|
VECTOR2I margin;
|
|
|
|
for( PCB_LAYER_ID layer : pad_layers )
|
|
{
|
|
bool onCopperLayer = ( LSET::AllCuMask() & LSET( { layer } ) ).any();
|
|
bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & LSET( { layer } ) ).any();
|
|
bool onSolderPasteLayer =
|
|
( LSET( { F_Paste, B_Paste } ) & LSET( { layer } ) ).any();
|
|
|
|
if( onSolderMaskLayer )
|
|
margin.x = margin.y = pad->GetSolderMaskExpansion( PADSTACK::ALL_LAYERS );
|
|
|
|
if( onSolderPasteLayer )
|
|
margin = pad->GetSolderPasteMargin( PADSTACK::ALL_LAYERS );
|
|
|
|
VECTOR2I padPlotsSize = pad->GetSize( PADSTACK::ALL_LAYERS ) + margin * 2;
|
|
|
|
if( onCopperLayer && !pad->IsOnCopperLayer() )
|
|
continue;
|
|
|
|
if( onCopperLayer && !pad->FlashLayer( layer ) )
|
|
continue;
|
|
|
|
if( pad->GetShape( PADSTACK::ALL_LAYERS ) != PAD_SHAPE::CUSTOM
|
|
&& ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
|
|
continue;
|
|
|
|
elements[layer][pad->GetNetCode()].push_back( pad );
|
|
}
|
|
}
|
|
}
|
|
|
|
for( const auto& [layerID, layerName] : m_plugin->GetLayerNameList() )
|
|
{
|
|
std::shared_ptr<ODB_LAYER_ENTITY> layer_entity_ptr = std::make_shared<ODB_LAYER_ENTITY>(
|
|
m_board, m_plugin, elements[layerID], layerID, layerName );
|
|
|
|
m_layerEntityMap.emplace( layerName, layer_entity_ptr );
|
|
}
|
|
}
|