mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2024-11-25 09:25:01 +00:00
978 lines
30 KiB
C++
978 lines
30 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2024 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" + GetMajorMinorPatchVersion() ) },
|
|
{ wxS( "CREATION_DATE" ), wxDateTime::Now().FormatISOCombined() },
|
|
{ wxS( "SAVE_DATE" ), wxDateTime::Now().FormatISOCombined() },
|
|
{ wxS( "SAVE_APP" ), wxS( "Pcbnew" ) },
|
|
{ wxS( "SAVE_USER" ), wxS( "" ) } };
|
|
}
|
|
|
|
|
|
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 );
|
|
}
|
|
}
|