ADDED: CADSTAR Parts Libraries (.lib)

This commit is contained in:
Roberto Fernandez Bautista 2023-03-07 22:38:15 +01:00
parent 8f83f27336
commit 6ab2112135
7 changed files with 481 additions and 210 deletions

View File

@ -383,8 +383,7 @@ struct MXP_LINE :
//[*SPI_[(<Part name>)]_[<Model>]_<Component Value>]
struct SPICE_PART_NAME : STRING_IN_BRACKETS {};
struct SPICE_FIRST : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {};
struct SPICE_SECOND : sor<QUOTED_STRING, STRING_EXCLUDING<WHITESPACE>> {};
struct SPICE_MODEL : sor<QUOTED_STRING, STRING_EXCLUDING<>> {};
struct SPI_LINE :
seq
<
@ -392,9 +391,8 @@ struct SPI_LINE :
TAO_PEGTL_ISTRING( "*SPI"),
plus<WHITESPACE>,
opt<SPICE_PART_NAME>,
plus<WHITESPACE>,
SPICE_FIRST, // Spice Value or Model
opt<plus<WHITESPACE>, SPICE_SECOND>, // Spice Value
star<WHITESPACE>,
SPICE_MODEL,
opt<eol>
>
{};

View File

@ -59,10 +59,9 @@ struct CADSTAR_PART_ENTRY
std::optional<std::string> m_SpicePartName;
std::optional<std::string> m_SpiceModel;
std::optional<std::string> m_SpiceValue;
std::optional<std::string> m_AcceptancePartName;
std::optional<std::string> m_AcceptanceText;
std::optional<std::string> m_AcceptancePartName; // The part the acceptance text refers to
std::optional<std::string> m_AcceptanceText; // "Part Acceptance" has canonical meaning
bool m_GateSwappingAllowed = true;
bool m_PinsVisible = true;

View File

@ -154,26 +154,11 @@ DEFINE_STRING_ACTION( ATTRIBUTE_NAME, m_CurrentAttrName );
DEFINE_STRING_ACTION( ACCEPTANCE_PART_NAME, m_CurrentPart.m_AcceptancePartName );
DEFINE_STRING_ACTION( ACCEPTANCE_TEXT, m_CurrentPart.m_AcceptanceText );
DEFINE_STRING_ACTION( SPICE_PART_NAME, m_CurrentPart.m_SpicePartName );
DEFINE_STRING_ACTION( SPICE_MODEL, m_CurrentPart.m_SpiceModel );
DEFINE_STRING_ACTION( SCH_NAME, m_CurrentSymbol.m_SymbolName );
DEFINE_STRING_ACTION( SCH_ALTERNATE, m_CurrentSymbol.m_SymbolAlternateName );
DEFINE_STRING_ACTION( PIN_SIGNAL_NAME, m_CurrentPin.m_Signal );
// Might become m_SpiceModel if SPICE_SECOND is found
DEFINE_STRING_ACTION( SPICE_FIRST, m_CurrentPart.m_SpiceValue );
template <>
struct CADSTAR_LIB_PARSER_ACTION<SPICE_SECOND>
{
/* @todo : convert to use apply0 to improve performance( once fully tested ) */
template <typename ActionInput>
static void apply( const ActionInput& in, CADSTAR_LIB_PARSER_STATE& s )
{
assert( in.string().size() >= s.m_CurrentString.size() );
s.m_CurrentPart.m_SpiceModel = s.m_CurrentPart.m_SpiceValue; // Parsed by SPICE_FIRST
s.m_CurrentPart.m_SpiceValue = s.m_CurrentString;
s.m_CurrentString = "";
}
};
// STRING SEGMENT action
// Any strings we match, append to the current state string (the state string gets

View File

@ -24,6 +24,7 @@
*/
#include <sch_plugins/cadstar/cadstar_sch_archive_loader.h>
#include <plugins/cadstar/cadstar_parts_lib_parser.h>
#include <bus_alias.h>
#include <core/mirror.h>
@ -46,11 +47,275 @@
#include <sch_sheet_pin.h>
#include <sch_label.h>
#include <schematic.h>
#include <sim/sim_model.h>
#include <trigo.h>
#include <wildcards_and_files_ext.h>
const wxString PartNameFieldName = "Part Name";
const wxString PartNumberFieldName = "Part Number";
const wxString PartVersionFieldName = "Part Version";
const wxString PartAcceptanceFieldName = "Part Acceptance";
std::vector<LIB_SYMBOL*> CADSTAR_SCH_ARCHIVE_LOADER::LoadPartsLib( const wxString& aFilename )
{
if( m_progressReporter )
m_progressReporter->SetNumPhases( 3 ); // (0) Read csa, (1) Parse csa, (3) Load lib
Parse();
CADSTAR_PARTS_LIB_PARSER p;
if( !p.CheckFileHeader( aFilename.utf8_string() ) )
THROW_IO_ERROR(
_( "The selected file does not appear to be a CADSTAR parts Library file" ) );
// TODO: we could add progress reporting for reading .lib
CADSTAR_PARTS_LIB_MODEL csLib = p.ReadFile( aFilename.utf8_string() );
if( m_progressReporter )
{
m_progressReporter->BeginPhase( 2 );
long numSteps = csLib.m_PartEntries.size();
m_progressReporter->SetMaxProgress( numSteps );
}
std::vector<LIB_SYMBOL*> retVal;
for( const CADSTAR_PART_ENTRY& part : csLib.m_PartEntries )
{
std::unique_ptr<LIB_SYMBOL> loadedPart = loadLibPart( part );
checkPoint();
if( loadedPart )
retVal.push_back( loadedPart.release() );
}
return retVal;
}
std::unique_ptr<LIB_SYMBOL>
CADSTAR_SCH_ARCHIVE_LOADER::loadLibPart( const CADSTAR_PART_ENTRY& aPart )
{
wxString escapedPartName = EscapeString( aPart.m_Name, CTX_LIBID );
std::unique_ptr<LIB_SYMBOL> retSym;
int unit = 0;
for( const CADSTAR_PART_SYMBOL_ENTRY& sym : aPart.m_Symbols )
{
++unit;
wxString alternateName = sym.m_SymbolAlternateName.value_or( "" );
SYMDEF_ID symbolID = getSymDefFromName( sym.m_SymbolName, alternateName );
if( !Library.SymbolDefinitions.count( symbolID ) )
{
m_reporter->Report(
wxString::Format( _( "Unable to find symbol %s, referenced by part %s. The "
"part was not loaded." ),
generateLibName( sym.m_SymbolName, alternateName ),
aPart.m_Name ),
RPT_SEVERITY_ERROR );
return nullptr;
}
// Load the graphical symbol for this gate
std::unique_ptr<LIB_SYMBOL> kiSymDef( loadSymdef( symbolID )->Duplicate() );
if( sym.m_Pins.size() != kiSymDef->GetPinCount() )
{
m_reporter->Report(
wxString::Format( _( "Inconsitent pin numbers in symbol %s compared to the one "
"defined in part %s. The part was not loaded." ),
generateLibName( sym.m_SymbolName, alternateName ),
aPart.m_Name ),
RPT_SEVERITY_ERROR );
return nullptr;
}
wxASSERT( m_symDefTerminalsMap.count( symbolID ) ); //loadSymDef should have populated this
// Update the pin numbers to match those defined in the Cadstar part
for( auto& [storedPinNum, termID] : m_symDefTerminalsMap[symbolID] )
{
wxCHECK( termID > 0 && sym.m_Pins.size() >= termID, nullptr );
LIB_PIN* pin = kiSymDef->GetPin( storedPinNum );
size_t termIdx = size_t( termID ) - 1;
// For now leave numerical pin number. Otherwise, when loading the
// .cpa file we won't be able to link up to the footprint pads, but if
// we solve this, we could then load alphanumeric pin numbers as below:
//
// if( aPart.m_PinNamesMap.count( termID ) )
// partPinNum = wxString( aPart.m_PinNamesMap.at( termID ) );
//
wxString partPinNum = wxString::Format( "%ld", sym.m_Pins[termIdx].m_Identifier );
pin->SetNumber( partPinNum );
if( aPart.m_PinNamesMap.count( termID ) )
pin->SetName( HandleTextOverbar( aPart.m_PinNamesMap.at( termID ) ) );
pin->SetType( getKiCadPinType( sym.m_Pins[termIdx].m_Type ) );
// @todo: Load pin/gate swapping information once kicad supports this
}
if( unit == 1 )
{
wxCHECK( kiSymDef->GetUnitCount() == 1, nullptr );
// The first unit can just be moved to the part symbol
retSym = std::move( kiSymDef );
retSym->SetUnitCount( aPart.m_Symbols.size(), true );
retSym->SetName( escapedPartName );
retSym->GetReferenceField().SetText( aPart.m_ComponentStem );
retSym->GetValueField().SetText( aPart.m_Value.value_or( "" ) );
addNewFieldToSymbol( PartNameFieldName, retSym )->SetText( aPart.m_Name );
retSym->SetDescription( aPart.m_Description.value_or( "" ) );
auto addFieldIfHasValue =
[&]( const wxString& aFieldName, const std::optional<std::string>& aFieldValue )
{
if( aFieldValue.has_value() )
addNewFieldToSymbol( aFieldName, retSym )->SetText( aFieldValue.value() );
};
addFieldIfHasValue( PartNumberFieldName, aPart.m_Number );
addFieldIfHasValue( PartVersionFieldName, aPart.m_Version );
addFieldIfHasValue( PartAcceptanceFieldName, aPart.m_AcceptancePartName );
setFootprintOnSymbol( retSym, aPart.m_Pcb_component,
aPart.m_Pcb_alternate.value_or( "" ) );
if(aPart.m_SpiceModel.has_value())
{
wxString modelVal = wxString::Format( "model=\"%s\"", aPart.m_SpiceModel.value() );
addNewFieldToSymbol( SIM_DEVICE_TYPE_FIELD, retSym )->SetText( "SPICE" );
addNewFieldToSymbol( SIM_PARAMS_FIELD, retSym )->SetText( modelVal );
}
// Load all part attributes, regardless of original cadstar type, to the symbol
// @todo some cadstar part attributes have a "read-only" flag. We should load this
// when KiCad supports read-only fields.
for( auto& [fieldName, value] : aPart.m_UserAttributes )
addNewFieldToSymbol( fieldName, retSym )->SetText( value );
for( auto& [fieldName, attrValue] : aPart.m_SchAttributes )
addNewFieldToSymbol( fieldName, retSym )->SetText( attrValue.m_Value );
for( auto& [fieldName, attrValue] : aPart.m_PcbAttributes )
addNewFieldToSymbol( fieldName, retSym )->SetText( attrValue.m_Value );
for( auto& [fieldName, attrValue] : aPart.m_SchAndPcbAttributes )
addNewFieldToSymbol( fieldName, retSym )->SetText( attrValue.m_Value );
for( auto& [fieldName, attrValue] : aPart.m_PartAttributes )
addNewFieldToSymbol( fieldName, retSym )->SetText( attrValue.m_Value );
// Load all hidden pins onto the first unit of the symbol in KiCad
// We load them in a spiral sequence, starting at the center of the symbol BBOX
VECTOR2I symCenter = retSym->GetBodyBoundingBox( unit, 0, false, false ).GetCenter();
symCenter.y = -symCenter.y; // need to invert the y coord for lib symbols.
VECTOR2I delta( 0, 1 );
VECTOR2I direction( 0, -1 );
int spacing = schIUScale.MilsToIU( 50 ); // for now, place on a 50mil grid
for( const CADSTAR_PART_PIN& csPin : aPart.m_HiddenPins )
{
std::unique_ptr<LIB_PIN> pin = std::make_unique<LIB_PIN>( retSym.get() );
long pinNum = csPin.m_Identifier;
pin->SetNumber( wxString::Format( "%ld", pinNum ) );
if( csPin.m_Signal.has_value() )
{
// Implied net connection
pin->SetName( csPin.m_Signal.value() );
pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
}
else if( aPart.m_PinNamesMap.count( pinNum ) )
{
pin->SetName( HandleTextOverbar( aPart.m_PinNamesMap.at( pinNum ) ) );
pin->SetType( getKiCadPinType( csPin.m_Type ) );
}
pin->SetVisible( false );
// Generate the coordinate for the pin. We don't want overlapping pins
// and ideally close to the center of the symbol, so we load pins sequentially
// in a spiral sequence
if( delta.x == delta.y
|| ( delta.x < 0 && delta.x == -delta.y )
|| ( delta.x > 0 && delta.x == 1 - delta.y ) )
{
// change direction
direction = { -direction.y, direction.x };
}
delta += direction;
VECTOR2I offset = delta * spacing;
pin->SetPosition( symCenter + offset );
pin->SetLength( 0 ); //CADSTAR Pins are just a point (have no length)
pin->SetShape( GRAPHIC_PINSHAPE::LINE );
pin->SetUnit( unit );
retSym->AddDrawItem( pin.release() );
}
}
else
{ // Source: Dest:
copySymbolItems( kiSymDef, retSym, unit, false /* aOverrideFields */ );
}
retSym->SetShowPinNames( aPart.m_PinsVisible );
retSym->SetShowPinNumbers( aPart.m_PinsVisible );
}
return retSym;
}
void CADSTAR_SCH_ARCHIVE_LOADER::copySymbolItems( std::unique_ptr<LIB_SYMBOL>& aSourceSym,
std::unique_ptr<LIB_SYMBOL>& aDestSym,
int aDestUnit, bool aOverrideFields )
{
// Ensure there are no items on the unit we want to load onto
for( LIB_ITEM* item : aDestSym->GetUnitDrawItems( aDestUnit, 0 /*aConvert*/ ) )
aDestSym->RemoveDrawItem( item );
// Copy all draw items
for( LIB_ITEM* newItem : aSourceSym->GetUnitDrawItems( 1, 0 /*aConvert*/ ) )
{
LIB_ITEM* itemCopy = static_cast<LIB_ITEM*>( newItem->Clone() );
itemCopy->SetParent( aDestSym.get() );
itemCopy->SetUnit( aDestUnit );
aDestSym->AddDrawItem( itemCopy );
}
//Copy / override all fields
if( aOverrideFields )
{
std::vector<LIB_FIELD*> fieldsToCopy;
aSourceSym->GetFields( fieldsToCopy );
for( LIB_FIELD* templateField : fieldsToCopy )
{
LIB_FIELD* appliedField = addNewFieldToSymbol( templateField->GetName(), aDestSym );
templateField->Copy( appliedField );
}
}
}
void CADSTAR_SCH_ARCHIVE_LOADER::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet )
@ -386,9 +651,9 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadPartsLibrary()
PART part = partPair.second;
wxString escapedPartName = EscapeString( part.Name, CTX_LIBID );
LIB_SYMBOL* kiPart = new LIB_SYMBOL( escapedPartName );
LIB_SYMBOL* kiSym = new LIB_SYMBOL( escapedPartName );
kiPart->SetUnitCount( part.Definition.GateSymbols.size() );
kiSym->SetUnitCount( part.Definition.GateSymbols.size() );
bool ok = true;
for( std::pair<GATE_ID, PART::DEFINITION::GATE> gatePair : part.Definition.GateSymbols )
@ -413,12 +678,12 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadPartsLibrary()
}
m_partSymbolsMap.insert( { { partID, gateID }, symbolID } );
loadSymbolGateAndPartFields( symbolID, &part, gateID, kiPart );
loadSymbolGateAndPartFields( symbolID, part, gateID, kiSym );
}
if( ok && part.Definition.GateSymbols.size() != 0 )
{
m_loadedSymbols.push_back( kiPart );
m_loadedSymbols.push_back( kiSym );
}
else
{
@ -437,7 +702,7 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadPartsLibrary()
// the part name, which is important to load
}
m_partMap.insert( { partID, kiPart } );
m_partMap.insert( { partID, kiSym } );
checkPoint();
}
@ -471,7 +736,7 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadSchematicSymbolInstances()
sym.GateID = wxT( "A" ); // Assume Gate "A" if unspecified
PART_GATE_ID partSymbolID = { sym.PartRef.RefID, sym.GateID };
LIB_SYMBOL* kiPart = m_partMap.at( sym.PartRef.RefID );
LIB_SYMBOL* kiSym = m_partMap.at( sym.PartRef.RefID );
bool copy = false;
// The symbol definition in the part either does not exist for this gate number
@ -480,13 +745,13 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadSchematicSymbolInstances()
if( m_partSymbolsMap.find( partSymbolID ) == m_partSymbolsMap.end()
|| m_partSymbolsMap.at( partSymbolID ) != sym.SymdefID )
{
kiPart = new LIB_SYMBOL( *kiPart ); // Make a copy
kiSym = new LIB_SYMBOL( *kiSym ); // Make a copy
copy = true;
const PART& part = Parts.PartDefinitions.at( sym.PartRef.RefID );
loadSymbolGateAndPartFields( sym.SymdefID, &part, sym.GateID, kiPart );
loadSymbolGateAndPartFields( sym.SymdefID, part, sym.GateID, kiSym );
}
LIB_SYMBOL* scaledPart = getScaledLibPart( kiPart, sym.ScaleRatioNumerator,
LIB_SYMBOL* scaledPart = getScaledLibPart( kiSym, sym.ScaleRatioNumerator,
sym.ScaleRatioDenominator );
EDA_ANGLE symOrient = ANGLE_0;
@ -495,7 +760,7 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadSchematicSymbolInstances()
delete scaledPart;
if( copy )
delete kiPart;
delete kiSym;
SCH_FIELD* refField = symbol->GetField( REFERENCE_FIELD );
@ -1320,16 +1585,28 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadTextVariables()
}
LIB_FIELD* CADSTAR_SCH_ARCHIVE_LOADER::addNewFieldToSymbol( const wxString& aFieldName,
LIB_SYMBOL* aKiCadSymbol )
LIB_FIELD*
CADSTAR_SCH_ARCHIVE_LOADER::addNewFieldToSymbol( const wxString& aFieldName,
std::unique_ptr<LIB_SYMBOL>& aKiCadSymbol )
{
int fieldID = aKiCadSymbol->GetFieldCount();
LIB_FIELD* field = new LIB_FIELD( aKiCadSymbol, fieldID );
field->SetName( aFieldName );
field->SetVisible( false );
aKiCadSymbol->AddField( field );
// First Check if field already exists
LIB_FIELD* existingField = aKiCadSymbol->FindField( aFieldName );
return field;
if( existingField != nullptr )
return existingField;
int newfieldID = aKiCadSymbol->GetFieldCount();
LIB_FIELD* newfield = new LIB_FIELD( aKiCadSymbol.get(), newfieldID );
newfield->SetName( aFieldName );
newfield->SetVisible( false );
aKiCadSymbol->AddField( newfield );
/*
@todo we should load that a field is a URL by checking if it starts with "Link"
e.g.:
if( aFieldName.Lower().StartsWith( "link" ) )
newfield->SetAsURL*/
return newfield;
}
@ -1485,7 +1762,7 @@ const LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSymdef( const SYMDEF_ID& aSymd
}
// Always add the part name field (even if it doesn't have a specific location defined)
addNewFieldToSymbol( PartNameFieldName, kiSym.get() );
addNewFieldToSymbol( PartNameFieldName, kiSym );
if( csSym.TextLocations.count( PART_NAME_ATTRID ) )
{
@ -1509,7 +1786,7 @@ const LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSymdef( const SYMDEF_ID& aSymd
}
wxString attributeName = getAttributeName( attributeId );
LIB_FIELD* field = addNewFieldToSymbol( attributeName, kiSym.get() );
LIB_FIELD* field = addNewFieldToSymbol( attributeName, kiSym );
applyToLibraryFieldAttribute( textLocation, csSym.Origin, field );
}
@ -1523,7 +1800,7 @@ const LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSymdef( const SYMDEF_ID& aSymd
}
wxString attributeName = getAttributeName( attributeId );
LIB_FIELD* field = addNewFieldToSymbol( attributeName, kiSym.get() );
LIB_FIELD* field = addNewFieldToSymbol( attributeName, kiSym );
if( attrValue.HasLocation )
applyToLibraryFieldAttribute( attrValue.AttributeLocation, csSym.Origin, field );
@ -1537,7 +1814,7 @@ const LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSymdef( const SYMDEF_ID& aSymd
void CADSTAR_SCH_ARCHIVE_LOADER::loadSymbolGateAndPartFields( const SYMDEF_ID& aSymdefID,
const PART* aCadstarPart,
const PART& aCadstarPart,
const GATE_ID& aGateID,
LIB_SYMBOL* aSymbol )
{
@ -1546,179 +1823,156 @@ void CADSTAR_SCH_ARCHIVE_LOADER::loadSymbolGateAndPartFields( const SYMDEF_ID& a
std::unique_ptr<LIB_SYMBOL> kiSymDef( loadSymdef( aSymdefID )->Duplicate() );
wxCHECK( kiSymDef, /*void*/ );
if( aCadstarPart )
//todo: need to use unique_ptr more. For now just create it here and release at end of function
std::unique_ptr<LIB_SYMBOL> tempSymbol( aSymbol );
// Update the pin numbers to match those defined in the Cadstar part
TERMINAL_TO_PINNUM_MAP pinNumMap;
for( auto&& [storedPinNum, termID] : m_symDefTerminalsMap[aSymdefID] )
{
// Update the pin numbers to match those defined in the Cadstar part
PART::DEFINITION::PIN csPin = getPartDefinitionPin( aCadstarPart, aGateID, termID );
LIB_PIN* pin = kiSymDef->GetPin( storedPinNum );
TERMINAL_TO_PINNUM_MAP pinNumMap;
wxString pinName = HandleTextOverbar( csPin.Label );
wxString pinNum = HandleTextOverbar( csPin.Name );
for( auto&& [storedPinNum, termID] : m_symDefTerminalsMap[aSymdefID] )
if( pinNum.IsEmpty() )
{
PART::DEFINITION::PIN csPin = getPartDefinitionPin( *aCadstarPart, aGateID, termID );
LIB_PIN* pin = kiSymDef->GetPin( storedPinNum );
wxString pinName = HandleTextOverbar( csPin.Label );
wxString pinNum = HandleTextOverbar( csPin.Name );
if( pinNum.IsEmpty() )
{
if( !csPin.Identifier.IsEmpty() )
pinNum = csPin.Identifier;
else if( csPin.ID == UNDEFINED_VALUE )
pinNum = wxString::Format( "%ld", termID );
else
pinNum = wxString::Format( "%ld", csPin.ID );
}
pin->SetType( getKiCadPinType( csPin.Type ) );
pin->SetNumber( pinNum );
pin->SetName( pinName );
pinNumMap.insert( { termID, pinNum } );
if( !csPin.Identifier.IsEmpty() )
pinNum = csPin.Identifier;
else if( csPin.ID == UNDEFINED_VALUE )
pinNum = wxString::Format( "%ld", termID );
else
pinNum = wxString::Format( "%ld", csPin.ID );
}
m_pinNumsMap.insert( { aCadstarPart->ID + aGateID, pinNumMap } );
pin->SetType( getKiCadPinType( csPin.Type ) );
pin->SetNumber( pinNum );
pin->SetName( pinName );
pinNumMap.insert( { termID, pinNum } );
}
m_pinNumsMap.insert( { aCadstarPart.ID + aGateID, pinNumMap } );
// COPY ITEMS
// Copy the items over to aSymbol
int gateNumber = getKiCadUnitNumberFromGate( aGateID );
// Ensure there are no items on the unit we want to load onto
for( LIB_ITEM* item : aSymbol->GetUnitDrawItems( gateNumber, 0 /*aConvert*/ ) )
aSymbol->RemoveDrawItem( item );
// Copy all draw items
for( LIB_ITEM* newItem : kiSymDef->GetUnitDrawItems( 1, 0 /*aConvert*/ ) )
{
LIB_ITEM* itemCopy = static_cast<LIB_ITEM*>( newItem->Clone() );
itemCopy->SetParent( aSymbol );
itemCopy->SetUnit( gateNumber );
aSymbol->AddDrawItem( itemCopy );
}
//Copy / override all fields
std::vector<LIB_FIELD*> fieldsToCopy;
kiSymDef->GetFields( fieldsToCopy );
for( LIB_FIELD* templateField : fieldsToCopy )
{
LIB_FIELD* appliedField = aSymbol->FindField( templateField->GetName() );
if( !appliedField )
appliedField = addNewFieldToSymbol( templateField->GetName(), aSymbol );
templateField->Copy( appliedField );
}
copySymbolItems( kiSymDef, tempSymbol, gateNumber );
// Hide the value field for now (it might get unhidden if an attribute exists in the cadstar
// design with the text "Value"
aSymbol->GetValueField().SetVisible( false );
tempSymbol->GetValueField().SetVisible( false );
if( aCadstarPart )
LIB_FIELD* partNameField = tempSymbol->FindField( PartNameFieldName );
if( partNameField )
partNameField->SetText( EscapeFieldText( aCadstarPart.Name ) );
const POINT& symDefOrigin = Library.SymbolDefinitions.at( aSymdefID ).Origin;
wxString footprintRefName = wxEmptyString;
wxString footprintAlternateName = wxEmptyString;
auto loadLibraryField = [&]( const ATTRIBUTE_VALUE& aAttributeVal )
{
LIB_FIELD* partNameField = aSymbol->FindField( PartNameFieldName );
wxString attrName = getAttributeName( aAttributeVal.AttributeID );
if( partNameField )
partNameField->SetText( EscapeFieldText( aCadstarPart->Name ) );
// Remove invalid field characters
wxString attributeValue = aAttributeVal.Value;
attributeValue.Replace( wxT( "\n" ), wxT( "\\n" ) );
attributeValue.Replace( wxT( "\r" ), wxT( "\\r" ) );
attributeValue.Replace( wxT( "\t" ), wxT( "\\t" ) );
const POINT& symDefOrigin = Library.SymbolDefinitions.at( aSymdefID ).Origin;
wxString footprintRefName = wxEmptyString;
wxString footprintAlternateName = wxEmptyString;
//TODO: Handle "links": In cadstar a field can be a "link" if its name starts
// with the characters "Link ". Need to figure out how to convert them to
// equivalent in KiCad.
auto loadLibraryField = [&]( const ATTRIBUTE_VALUE& aAttributeVal )
if( attrName == wxT( "(PartDefinitionNameStem)" ) )
{
wxString attrName = getAttributeName( aAttributeVal.AttributeID );
// Remove invalid field characters
wxString attributeValue = aAttributeVal.Value;
attributeValue.Replace( wxT( "\n" ), wxT( "\\n" ) );
attributeValue.Replace( wxT( "\r" ), wxT( "\\r" ) );
attributeValue.Replace( wxT( "\t" ), wxT( "\\t" ) );
//TODO: Handle "links": In cadstar a field can be a "link" if its name starts
// with the characters "Link ". Need to figure out how to convert them to
// equivalent in KiCad.
if( attrName == wxT( "(PartDefinitionNameStem)" ) )
{
//Space not allowed in Reference field
attributeValue.Replace( wxT( " " ), "_" );
aSymbol->GetReferenceField().SetText( attributeValue );
return;
}
else if( attrName == wxT( "(PartDescription)" ) )
{
aSymbol->SetDescription( attributeValue );
return;
}
else if( attrName == wxT( "(PartDefinitionReferenceName)" ) )
{
footprintRefName = attributeValue;
return;
}
else if( attrName == wxT( "(PartDefinitionAlternateName)" ) )
{
footprintAlternateName = attributeValue;
return;
}
LIB_FIELD* attrField = aSymbol->FindField( attrName );
bool existsInSymbol = attrField != nullptr;
if( !attrField )
attrField = addNewFieldToSymbol( attrName, aSymbol );
wxASSERT( attrField->GetName() == attrName );
attrField->SetText( aAttributeVal.Value );
attrField->SetUnit( gateNumber );
const ATTRIBUTE_ID& attrid = aAttributeVal.AttributeID;
attrField->SetVisible( isAttributeVisible( attrid ) );
if( aAttributeVal.HasLocation )
{
// Check if the part itself defined a location for the field
applyToLibraryFieldAttribute( aAttributeVal.AttributeLocation, symDefOrigin,
attrField );
}
else if( !existsInSymbol )
{
attrField->SetVisible( false );
applyTextSettings( attrField, wxT( "TC1" ), ALIGNMENT::NO_ALIGNMENT,
JUSTIFICATION::LEFT );
}
};
// Load all attributes in the Part Definition
for( auto& [attrId, attrVal] : aCadstarPart->Definition.AttributeValues )
loadLibraryField( attrVal );
// Load all attributes in the Part itself.
for( auto& [attrId, attrVal] : aCadstarPart->AttributeValues )
loadLibraryField( attrVal );
wxString fpNameInLibrary = generateLibName( footprintRefName, footprintAlternateName );
if( !fpNameInLibrary.IsEmpty() )
//Space not allowed in Reference field
attributeValue.Replace( wxT( " " ), "_" );
tempSymbol->GetReferenceField().SetText( attributeValue );
return;
}
else if( attrName == wxT( "(PartDescription)" ) )
{
wxArrayString fpFilters;
tempSymbol->SetDescription( attributeValue );
return;
}
else if( attrName == wxT( "(PartDefinitionReferenceName)" ) )
{
footprintRefName = attributeValue;
return;
}
else if( attrName == wxT( "(PartDefinitionAlternateName)" ) )
{
footprintAlternateName = attributeValue;
return;
}
bool attrIsNew = tempSymbol->FindField( attrName ) == nullptr;
LIB_FIELD* attrField = addNewFieldToSymbol( attrName, tempSymbol );
wxASSERT( attrField->GetName() == attrName );
attrField->SetText( aAttributeVal.Value );
attrField->SetUnit( gateNumber );
const ATTRIBUTE_ID& attrid = aAttributeVal.AttributeID;
attrField->SetVisible( isAttributeVisible( attrid ) );
if( aAttributeVal.HasLocation )
{
// Check if the part itself defined a location for the field
applyToLibraryFieldAttribute( aAttributeVal.AttributeLocation, symDefOrigin,
attrField );
}
else if( attrIsNew )
{
attrField->SetVisible( false );
applyTextSettings( attrField, wxT( "TC1" ), ALIGNMENT::NO_ALIGNMENT,
JUSTIFICATION::LEFT );
}
};
// Load all attributes in the Part Definition
for( auto& [attrId, attrVal] : aCadstarPart.Definition.AttributeValues )
loadLibraryField( attrVal );
// Load all attributes in the Part itself.
for( auto& [attrId, attrVal] : aCadstarPart.AttributeValues )
loadLibraryField( attrVal );
setFootprintOnSymbol( tempSymbol, footprintRefName, footprintAlternateName );
if( aCadstarPart.Definition.HidePinNames )
{
tempSymbol->SetShowPinNames( false );
tempSymbol->SetShowPinNumbers( false );
}
aSymbol = tempSymbol.release();
}
void CADSTAR_SCH_ARCHIVE_LOADER::setFootprintOnSymbol( std::unique_ptr<LIB_SYMBOL>& aKiCadSymbol,
const wxString& aFootprintName,
const wxString& aFootprintAlternate )
{
wxString fpNameInLibrary = generateLibName( aFootprintName, aFootprintAlternate );
if( !fpNameInLibrary.IsEmpty() )
{
wxArrayString fpFilters;
fpFilters.Add( aFootprintName ); // In cadstar one footprint has several "alternates"
if( !aFootprintAlternate.IsEmpty() )
fpFilters.Add( fpNameInLibrary );
aSymbol->SetFPFilters( fpFilters );
// Assume that the PCB footprint library name will be the same as the schematic filename
wxFileName schFilename( Filename );
wxString libName = schFilename.GetName();
aSymbol->GetFootprintField().SetText( libName + wxT( ":" ) + fpNameInLibrary );
}
aKiCadSymbol->SetFPFilters( fpFilters );
if( aCadstarPart->Definition.HidePinNames )
{
aSymbol->SetShowPinNames( false );
aSymbol->SetShowPinNumbers( false );
}
LIB_ID libID( m_footprintLibName, fpNameInLibrary );
aKiCadSymbol->GetFootprintField().SetText( libID.Format() );
}
}

View File

@ -38,6 +38,7 @@
#include <wx/string.h>
class BUS_ALIAS;
class CADSTAR_PART_ENTRY;
class EDA_TEXT;
class TEXT_SPIN_STYLE;
class LIB_FIELD;
@ -70,6 +71,10 @@ public:
m_designCenter.y = 0;
m_reporter = aReporter;
m_progressReporter = aProgressReporter;
// Assume that the PCB footprint library name will be the same as the schematic filename
wxFileName schFilename( Filename );
m_footprintLibName = schFilename.GetName();
}
@ -77,8 +82,13 @@ public:
{
}
std::vector<LIB_SYMBOL*> LoadPartsLib( const wxString& aFilename );
const std::vector<LIB_SYMBOL*>& GetLoadedSymbols() const { return m_loadedSymbols; }
void SetFpLibName( const wxString& aLibName ) { m_footprintLibName = aLibName; };
/**
* @brief Loads a CADSTAR Schematic Archive file into the KiCad SCHEMATIC object given
* @param aSchematic Schematic to add the design onto
@ -99,9 +109,10 @@ private:
typedef std::map<wxString, TERMINAL_ID> PINNUM_TO_TERMINAL_MAP;
REPORTER* m_reporter;
SCHEMATIC* m_schematic;
SCH_SHEET* m_rootSheet;
REPORTER* m_reporter;
SCHEMATIC* m_schematic;
SCH_SHEET* m_rootSheet;
wxString m_footprintLibName; ///< Name of the footprint library to prepend all footprints with
/**
* Required for calculating the offset to apply to the Cadstar design so that it fits
@ -141,6 +152,12 @@ private:
void loadDocumentationSymbols();
void loadTextVariables();
std::unique_ptr<LIB_SYMBOL> loadLibPart( const CADSTAR_PART_ENTRY& aPart );
void copySymbolItems( std::unique_ptr<LIB_SYMBOL>& aSourceSym,
std::unique_ptr<LIB_SYMBOL>& aDestSym, int aDestUnit,
bool aOverrideFields = true );
//Helper Functions for loading sheets
void loadSheetAndChildSheets( LAYER_ID aCadstarSheetID, const VECTOR2I& aPosition,
VECTOR2I aSheetSize, const SCH_SHEET_PATH& aParentSheet );
@ -156,9 +173,13 @@ private:
//Helper Functions for loading library items
const LIB_SYMBOL* loadSymdef( const SYMDEF_ID& aSymdefID );
void loadSymbolGateAndPartFields( const SYMDEF_ID& aSymdefID, const PART* aCadstarPart,
void loadSymbolGateAndPartFields( const SYMDEF_ID& aSymdefID, const PART& aCadstarPart,
const GATE_ID& aGateID, LIB_SYMBOL* aSymbol );
void setFootprintOnSymbol( std::unique_ptr<LIB_SYMBOL>& aKiCadSymbol,
const wxString& aFootprintName,
const wxString& aFootprintAlternate );
void loadLibrarySymbolShapeVertices( const std::vector<VERTEX>& aCadstarVertices,
VECTOR2I aSymbolOrigin, LIB_SYMBOL* aSymbol,
int aGateNumber, int aLineThickness );
@ -304,7 +325,8 @@ private:
double getPolarRadius( const VECTOR2I& aPoint );
static LIB_FIELD* addNewFieldToSymbol( const wxString& aFieldName, LIB_SYMBOL* aKiCadSymbol );
static LIB_FIELD* addNewFieldToSymbol( const wxString& aFieldName,
std::unique_ptr<LIB_SYMBOL>& aKiCadSymbol );
}; // CADSTAR_SCH_ARCHIVE_LOADER

View File

@ -197,17 +197,23 @@ void CADSTAR_SCH_ARCHIVE_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& a
const wxString& aLibraryPath,
const STRING_UTF8_MAP* aProperties )
{
static std::vector<LIB_SYMBOL*> cached;
static wxString cachedPath;
if(cachedPath == aLibraryPath)
{
aSymbolList = cached;
return;
}
wxFileName fn( aLibraryPath );
fn.SetExt( "csa" );
fn.SetName( "symbol" );
CADSTAR_SCH_ARCHIVE_LOADER csaLoader( fn.GetFullPath(), m_reporter, m_progressReporter );
if( m_progressReporter )
m_progressReporter->SetNumPhases( 2 ); // (0) Read file, (1) Parse file
csaLoader.Parse();
printf( "symbols: %zd", csaLoader.Library.SymbolDefinitions.size() );
aSymbolList = csaLoader.LoadPartsLib( aLibraryPath );
cachedPath = aLibraryPath;
cached = aSymbolList;
}
@ -215,6 +221,15 @@ LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_PLUGIN::LoadSymbol( const wxString& aLibr
const wxString& aAliasName,
const STRING_UTF8_MAP* aProperties )
{
std::vector<LIB_SYMBOL*> symbols;
EnumerateSymbolLib( symbols, aLibraryPath, aProperties );
for( LIB_SYMBOL*& sym : symbols )
{
if( sym->GetName() == aAliasName )
return sym;
}
return nullptr;
}

View File

@ -127,8 +127,7 @@ BOOST_AUTO_TEST_CASE( ReadFile )
BOOST_CHECK_EQUAL( partEntry.m_PinsVisible, i % 5 != 1 );
BOOST_CHECK_EQUAL( partEntry.m_SpicePartName, "PartName" + std::to_string( i ) );
BOOST_CHECK_EQUAL( partEntry.m_SpiceModel, std::optional<std::string>() );
BOOST_CHECK_EQUAL( partEntry.m_SpiceValue, std::to_string( i ) + "uH" );
BOOST_CHECK_EQUAL( partEntry.m_SpiceModel, std::to_string( i ) + "uH" );
BOOST_CHECK_EQUAL( partEntry.m_AcceptancePartName, "PartName" + std::to_string( i ) );
BOOST_CHECK_EQUAL( partEntry.m_AcceptanceText, "Acceptance" + std::to_string( i ) );
@ -341,8 +340,7 @@ BOOST_AUTO_TEST_CASE( ReadContent )
// Check *SPI
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpicePartName, "<Part name>" );
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpiceModel, "<Model>" );
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpiceValue, "<Value>" );
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_SpiceModel, "<Model> <Value>" );
// Check *PAC
BOOST_CHECK_EQUAL( result.m_PartEntries[0].m_AcceptancePartName, "<Part name>" );