diff --git a/common/netlist.keywords b/common/netlist.keywords index f5d39cd67f..edabd96803 100644 --- a/common/netlist.keywords +++ b/common/netlist.keywords @@ -28,6 +28,7 @@ part pin pins pinfunction +property ref sheetpath source diff --git a/eeschema/netlist_exporters/netlist_exporter_generic.cpp b/eeschema/netlist_exporters/netlist_exporter_generic.cpp index 7d246d6743..168acf645b 100644 --- a/eeschema/netlist_exporters/netlist_exporter_generic.cpp +++ b/eeschema/netlist_exporters/netlist_exporter_generic.cpp @@ -205,9 +205,9 @@ XNODE* NETLIST_EXPORTER_GENERIC::makeComponents( unsigned aCtl ) // Output is xml, so there is no reason to remove spaces from the field values. // And XML element names need not be translated to various languages. - for( unsigned i = 0; i < sheetList.size(); i++ ) + for( unsigned ii = 0; ii < sheetList.size(); ii++ ) { - SCH_SHEET_PATH sheet = sheetList[i]; + SCH_SHEET_PATH sheet = sheetList[ii]; auto cmp = [sheet]( SCH_COMPONENT* a, SCH_COMPONENT* b ) @@ -218,10 +218,10 @@ XNODE* NETLIST_EXPORTER_GENERIC::makeComponents( unsigned aCtl ) std::set ordered_components( cmp ); - for( auto item : sheetList[i].LastScreen()->Items().OfType( SCH_COMPONENT_T ) ) + for( SCH_ITEM* item : sheetList[ii].LastScreen()->Items().OfType( SCH_COMPONENT_T ) ) { - auto comp = static_cast( item ); - auto test = ordered_components.insert( comp ); + SCH_COMPONENT* comp = static_cast( item ); + auto test = ordered_components.insert( comp ); if( !test.second ) { @@ -233,7 +233,7 @@ XNODE* NETLIST_EXPORTER_GENERIC::makeComponents( unsigned aCtl ) } } - for( auto item : ordered_components ) + for( EDA_ITEM* item : ordered_components ) { SCH_COMPONENT* comp = findNextComponent( item, &sheet ); @@ -242,17 +242,16 @@ XNODE* NETLIST_EXPORTER_GENERIC::makeComponents( unsigned aCtl ) || ( ( aCtl & GNL_OPT_KICAD ) && !comp->GetIncludeOnBoard() ) ) continue; - XNODE* xcomp; // current component being constructed - // Output the component's elements in order of expected access frequency. // This may not always look best, but it will allow faster execution // under XSL processing systems which do sequential searching within // an element. + XNODE* xcomp; // current component being constructed xcomps->AddChild( xcomp = node( "comp" ) ); - xcomp->AddAttribute( "ref", comp->GetRef( &sheet ) ); - addComponentFields( xcomp, comp, &sheetList[i] ); + xcomp->AddAttribute( "ref", comp->GetRef( &sheet ) ); + addComponentFields( xcomp, comp, &sheetList[ii] ); XNODE* xlibsource; xcomp->AddChild( xlibsource = node( "libsource" ) ); @@ -268,9 +267,20 @@ XNODE* NETLIST_EXPORTER_GENERIC::makeComponents( unsigned aCtl ) xlibsource->AddAttribute( "description", comp->GetDescription() ); - XNODE* xsheetpath; + std::vector& fields = comp->GetFields(); + for( size_t jj = MANDATORY_FIELDS; jj < fields.size(); ++jj ) + { + XNODE* xproperty; + xcomp->AddChild( xproperty = node( "property" ) ); + + xproperty->AddAttribute( "name", fields[jj].GetName() ); + xproperty->AddAttribute( "value", fields[jj].GetText() ); + } + + XNODE* xsheetpath; xcomp->AddChild( xsheetpath = node( "sheetpath" ) ); + xsheetpath->AddAttribute( "names", sheet.PathHumanReadable() ); xsheetpath->AddAttribute( "tstamps", sheet.PathAsString() ); xcomp->AddChild( node( "tstamp", comp->m_Uuid.AsString() ) ); diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 5a8f5babf7..c988d6a30b 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -373,6 +373,11 @@ bool MODULE::ResolveTextVar( wxString* token, int aDepth ) const *token = GetLayerName(); return true; } + else if( m_properties.count( *token ) ) + { + *token = m_properties.at( *token ); + return true; + } return false; } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index bb17ac7440..aaf822e4c8 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -488,6 +488,9 @@ public: TEXTE_MODULE& Value() const { return *m_Value; } TEXTE_MODULE& Reference() const { return *m_Reference; } + const std::map& GetProperties() const { return m_properties; } + void SetProperties( const std::map& aProps ) { m_properties = aProps; } + /** * Function FindPadByName * returns a D_PAD* with a matching name. Note that names may not be @@ -696,10 +699,9 @@ public: #endif private: - DRAWINGS m_drawings; // BOARD_ITEMs for drawings on the board, owned by pointer. - PADS m_pads; // D_PAD items, owned by pointer + DRAWINGS m_drawings; // BOARD_ITEMs for drawings on the board, owned by pointer. + PADS m_pads; // D_PAD items, owned by pointer MODULE_ZONE_CONTAINERS m_fp_zones; // MODULE_ZONE_CONTAINER items, owned by pointer - std::list m_3D_Drawings; // Linked list of 3D models. double m_Orient; // Orientation in tenths of a degree, 900=90.0 degrees. wxPoint m_Pos; // Position of module on the board in internal units. @@ -727,13 +729,13 @@ private: int m_CntRot90; // Horizontal automatic placement cost ( 0..10 ). int m_CntRot180; // Vertical automatic placement cost ( 0..10 ). - wxArrayString* m_initial_comments; ///< leading s-expression comments in the module, - ///< lazily allocated only if needed for speed + std::list m_3D_Drawings; // Linked list of 3D models. + std::map m_properties; + wxArrayString* m_initial_comments; // s-expression comments in the module, + // lazily allocated only if needed for speed - /// Used in DRC to test the courtyard area (a polygon which can be not basic - /// Note also a footprint can have courtyards on both board sides - SHAPE_POLY_SET m_poly_courtyard_front; - SHAPE_POLY_SET m_poly_courtyard_back; + SHAPE_POLY_SET m_poly_courtyard_front; // Note that a module can have both front and back + SHAPE_POLY_SET m_poly_courtyard_back; // courtyards populated. }; #endif // MODULE_H_ diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 39aa354513..d3b74dc2fd 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -954,6 +954,15 @@ void PCB_IO::format( MODULE* aModule, int aNestLevel ) const m_out->Print( aNestLevel+1, "(tags %s)\n", m_out->Quotew( aModule->GetKeywords() ).c_str() ); + const std::map& props = aModule->GetProperties(); + + for( const std::pair& prop : props ) + { + m_out->Print( aNestLevel+1, "(property %s %s)\n", + m_out->Quotew( prop.first ).c_str(), + m_out->Quotew( prop.second ).c_str() ); + } + if( !( m_ctl & CTL_OMIT_PATH ) && !aModule->GetPath().empty() ) m_out->Print( aNestLevel+1, "(path %s)\n", m_out->Quotew( aModule->GetPath().AsString() ).c_str() ); diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index dcefbd8c92..4793155d68 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -73,7 +73,8 @@ class TEXTE_PCB; //#define SEXPR_BOARD_FILE_VERSION 20200625 // Multilayer zones, zone names, island controls //#define SEXPR_BOARD_FILE_VERSION 20200628 // remove visibility settings //#define SEXPR_BOARD_FILE_VERSION 20200724 // Add KIID to module components -#define SEXPR_BOARD_FILE_VERSION 20200807 // Add zone hatch advanced settings +//#define SEXPR_BOARD_FILE_VERSION 20200807 // Add zone hatch advanced settings +#define SEXPR_BOARD_FILE_VERSION 20200808 // Add properties to modules #define CTL_STD_LAYER_NAMES (1 << 0) ///< Use English Standard layer names #define CTL_OMIT_NETS (1 << 1) ///< Omit pads net names (useless in library) diff --git a/pcbnew/netlist_reader/board_netlist_updater.cpp b/pcbnew/netlist_reader/board_netlist_updater.cpp index ab3dfeb914..15104454a4 100644 --- a/pcbnew/netlist_reader/board_netlist_updater.cpp +++ b/pcbnew/netlist_reader/board_netlist_updater.cpp @@ -285,7 +285,7 @@ bool BOARD_NETLIST_UPDATER::updateComponentParameters( MODULE* aPcbComponent, aPcbComponent->GetReference(), aPcbComponent->GetPath().AsString(), aNewComponent->GetPath().AsString() ); - m_reporter->Report( msg, RPT_SEVERITY_INFO ); + m_reporter->Report( msg, RPT_SEVERITY_ACTION ); if( !m_isDryRun ) { @@ -294,6 +294,19 @@ bool BOARD_NETLIST_UPDATER::updateComponentParameters( MODULE* aPcbComponent, } } + if( aPcbComponent->GetProperties() != aNewComponent->GetProperties() ) + { + msg.Printf( _( "Update %s properties." ), + aPcbComponent->GetReference() ); + m_reporter->Report( msg, RPT_SEVERITY_ACTION ); + + if( !m_isDryRun ) + { + changed = true; + aPcbComponent->SetProperties( aNewComponent->GetProperties() ); + } + } + if( changed && copy ) m_commit.Modified( aPcbComponent, copy ); else diff --git a/pcbnew/netlist_reader/kicad_netlist_reader.cpp b/pcbnew/netlist_reader/kicad_netlist_reader.cpp index 37f54a97cd..0ebb0d7d70 100644 --- a/pcbnew/netlist_reader/kicad_netlist_reader.cpp +++ b/pcbnew/netlist_reader/kicad_netlist_reader.cpp @@ -282,11 +282,12 @@ void KICAD_NETLIST_PARSER::parseComponent() { /* Parses a section like * (comp (ref P1) - * (value DB25FEMALE) - * (footprint DB25FC) - * (libsource (lib conn) (part DB25)) - * (sheetpath (names /) (tstamps /)) - * (tstamp 68183921-93a5-49ac-91b0-49d05a0e1647)) + * (value DB25FEMALE) + * (footprint DB25FC) + * (libsource (lib conn) (part DB25)) + * (property (name PINCOUNT) (value 25)) + * (sheetpath (names /) (tstamps /)) + * (tstamp 68183921-93a5-49ac-91b0-49d05a0e1647)) * * other fields (unused) are skipped * A component need a reference, value, footprint name and a full time stamp @@ -300,6 +301,7 @@ void KICAD_NETLIST_PARSER::parseComponent() wxString name; KIID_PATH path; KIID uuid; + std::map properties; // The token comp was read, so the next data is (ref P1) while( (token = NextTok()) != T_RIGHT ) @@ -329,7 +331,7 @@ void KICAD_NETLIST_PARSER::parseComponent() case T_libsource: // Read libsource - while( (token = NextTok()) != T_RIGHT ) + while( ( token = NextTok() ) != T_RIGHT ) { if( token == T_LEFT ) token = NextTok(); @@ -358,6 +360,39 @@ void KICAD_NETLIST_PARSER::parseComponent() } break; + case T_property: + { + wxString propName; + wxString propValue; + + while( (token = NextTok() ) != T_RIGHT ) + { + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_name ) + { + NeedSYMBOLorNUMBER(); + propName = FROM_UTF8( CurText() ); + NeedRIGHT(); + } + else if( token == T_value ) + { + NeedSYMBOLorNUMBER(); + propValue = FROM_UTF8( CurText() ); + NeedRIGHT(); + } + else + { + Expecting( "name or value" ); + } + } + + if( !propName.IsEmpty() ) + properties[ propName ] = propValue; + } + break; + case T_sheetpath: while( ( token = NextTok() ) != T_EOF ) { @@ -397,6 +432,7 @@ void KICAD_NETLIST_PARSER::parseComponent() COMPONENT* component = new COMPONENT( fpid, ref, value, path ); component->SetName( name ); component->SetLibrary( library ); + component->SetProperties( properties ); m_netlist->AddComponent( component ); } diff --git a/pcbnew/netlist_reader/netlist_reader.h b/pcbnew/netlist_reader/netlist_reader.h index 13b3e0c313..b2d5a051a5 100644 --- a/pcbnew/netlist_reader/netlist_reader.h +++ b/pcbnew/netlist_reader/netlist_reader.h @@ -309,11 +309,12 @@ private: * Function parseComponent * parse a component description: * (comp (ref P1) - * (value DB25FEMELLE) - * (footprint DB25FC) - * (libsource (lib conn) (part DB25)) - * (sheetpath (names /) (tstamps /)) - * (tstamp 3256759C)) + * (value DB25FEMELLE) + * (footprint DB25FC) + * (libsource (lib conn) (part DB25)) + * (property (name PINCOUNT) (value 25)) + * (sheetpath (names /) (tstamps /)) + * (tstamp 3256759C)) */ void parseComponent(); @@ -321,9 +322,9 @@ private: * Function parseNet * Parses a section like * (net (code 20) (name /PC-A0) - * (node (ref BUS1) (pin 62)) - * (node (ref U3) (pin 3)) - * (node (ref U9) (pin M6))) + * (node (ref BUS1) (pin 62)) + * (node (ref U3) (pin 3)) + * (node (ref U9) (pin M6))) * * and set the corresponding pads netnames */ diff --git a/pcbnew/netlist_reader/pcb_netlist.cpp b/pcbnew/netlist_reader/pcb_netlist.cpp index 28e0f352a6..0fccea2d6c 100644 --- a/pcbnew/netlist_reader/pcb_netlist.cpp +++ b/pcbnew/netlist_reader/pcb_netlist.cpp @@ -48,6 +48,7 @@ void COMPONENT::SetModule( MODULE* aModule ) aModule->SetValue( m_value ); aModule->SetFPID( m_fpid ); aModule->SetPath( m_path ); + aModule->SetProperties( m_properties ); } @@ -56,10 +57,10 @@ COMPONENT_NET COMPONENT::m_emptyNet; const COMPONENT_NET& COMPONENT::GetNet( const wxString& aPinName ) const { - for( unsigned i = 0; i < m_nets.size(); i++ ) + for( const COMPONENT_NET& net : m_nets ) { - if( m_nets[i].GetPinName() == aPinName ) - return m_nets[i]; + if( net.GetPinName() == aPinName ) + return net; } return m_emptyNet; diff --git a/pcbnew/netlist_reader/pcb_netlist.h b/pcbnew/netlist_reader/pcb_netlist.h index e3eb7cc53f..7a07be5a56 100644 --- a/pcbnew/netlist_reader/pcb_netlist.h +++ b/pcbnew/netlist_reader/pcb_netlist.h @@ -105,6 +105,9 @@ class COMPONENT /// The #MODULE loaded for #m_fpid. std::unique_ptr< MODULE > m_footprint; + /// Component-specific properties found in the netlist. + std::map m_properties; + static COMPONENT_NET m_emptyNet; public: @@ -142,9 +145,14 @@ public: const wxString& GetLibrary() const { return m_library; } const wxString& GetReference() const { return m_reference; } - const wxString& GetValue() const { return m_value; } + void SetProperties( std::map& aProps ) + { + m_properties = std::move( aProps ); + } + const std::map& GetProperties() const { return m_properties; } + void SetFPID( const LIB_ID& aFPID ) { m_fpid = aFPID; } const LIB_ID& GetFPID() const { return m_fpid; } diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index 55db4553c4..4857ac79da 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -2454,6 +2454,8 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments ) std::unique_ptr module( new MODULE( m_board ) ); + std::map properties; + module->SetInitialComments( aInitialComments ); token = NextTok(); @@ -2550,6 +2552,14 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments ) NeedRIGHT(); break; + case T_property: + NeedSYMBOL(); + name = FromUTF8(); + NeedSYMBOL(); + properties[ name ] = FromUTF8(); + NeedRIGHT(); + break; + case T_path: NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here module->SetPath( KIID_PATH( FromUTF8() ) ); @@ -2717,6 +2727,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments ) } module->SetFPID( fpid ); + module->SetProperties( properties ); module->CalculateBoundingBox(); return module.release();