/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020-2021 Roberto Fernandez Bautista * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * * 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 . */ /** * @file cadstar_archive_parser.h * @brief Helper functions and common defines between schematic and PCB Archive files */ #ifndef CADSTAR_ARCHIVE_PARSER_H_ #define CADSTAR_ARCHIVE_PARSER_H_ #include #include #include #include #include #include #include #include // THROW_IO_ERROR definitions to ensure consistent wording is used in the error messages #define THROW_MISSING_NODE_IO_ERROR( nodename, location ) \ THROW_IO_ERROR( wxString::Format( _( "Missing node '%s' in '%s'" ), nodename, location ) ) #define THROW_UNKNOWN_NODE_IO_ERROR( nodename, location ) \ THROW_IO_ERROR( wxString::Format( _( "Unknown node '%s' in '%s'" ), nodename, location ) ) #define THROW_MISSING_PARAMETER_IO_ERROR( param, location ) \ THROW_IO_ERROR( wxString::Format( _( "Missing Parameter '%s' in '%s'" ), param, location ) ) #define THROW_UNKNOWN_PARAMETER_IO_ERROR( param, location ) \ THROW_IO_ERROR( wxString::Format( _( "Unknown Parameter '%s' in '%s'" ), param, location ) ) #define THROW_PARSING_IO_ERROR( param, location ) \ THROW_IO_ERROR( wxString::Format( _( "Unable to parse '%s' in '%s'" ), param, location ) ) //================================= // MACRO DEFINITIONS //================================= #define UNDEFINED_LAYER_ID ( LAYER_ID ) wxEmptyString /** * Component Name Attribute ID - typically used for placement of designators on silk screen. */ #define COMPONENT_NAME_ATTRID ( ATTRIBUTE_ID ) wxT( "__COMPONENT_NAME__" ) /** * Component Name 2 Attribute ID - typically used for indicating the placement of designators in * placement drawings. */ #define COMPONENT_NAME_2_ATTRID ( ATTRIBUTE_ID ) wxT( "__COMPONENT_NAME_2__" ) /** * Symbol Name attribute ID - used for placement of designators on the schematic */ #define SYMBOL_NAME_ATTRID ( ATTRIBUTE_ID ) wxT( "__SYMBOL_NAME__" ) #define LINK_ORIGIN_ATTRID ( ATTRIBUTE_ID ) wxT( "__LINK_ORIGIN__" ) #define SIGNALNAME_ORIGIN_ATTRID ( ATTRIBUTE_ID ) wxT( "__SIGNALNAME_ORIGIN__" ) #define PART_NAME_ATTRID ( ATTRIBUTE_ID ) wxT( "__PART_NAME__" ) class EDA_TEXT; class wxXmlAttribute; class PROGRESS_REPORTER; class SHAPE_LINE_CHAIN; class SHAPE_POLY_SET; /** * @brief Helper functions and common structures for CADSTAR PCB and Schematic archive files. */ class CADSTAR_ARCHIVE_PARSER { public: CADSTAR_ARCHIVE_PARSER() { m_progressReporter = nullptr; } typedef wxString LINECODE_ID; typedef wxString HATCHCODE_ID; typedef wxString ROUTECODE_ID; typedef wxString NETCLASS_ID; typedef wxString SPACING_CLASS_ID; typedef wxString TEXTCODE_ID; typedef wxString LAYER_ID; ///< ID of a Sheet (if schematic) or board Layer (if PCB) typedef wxString VARIANT_ID; typedef wxString ATTRIBUTE_ID; typedef wxString SYMDEF_ID; typedef wxString PART_ID; typedef wxString GATE_ID; typedef long TERMINAL_ID; ///< Terminal is the pin identifier in the schematic typedef long PART_DEFINITION_PIN_ID; ///< Pin identifier in the part definition typedef long PART_PIN_ID; ///< Pin identifier in the part typedef wxString TEXT_ID; typedef wxString FIGURE_ID; typedef wxString GROUP_ID; typedef wxString REUSEBLOCK_ID; typedef wxString NET_ID; typedef wxString NETELEMENT_ID; typedef wxString DOCUMENTATION_SYMBOL_ID; typedef wxString COLOR_ID; static const long UNDEFINED_VALUE = -1; /** * CADSTAR fonts are drawn on a 24x24 integer matrix, where the each axis goes from 0 to 24. * The characters can each specify a width of between 12 and 24, but the height is fixed at 24. * * The default CADSTAR font uses y=5 as the starting point for capital letters, leaving space * for the tails of letters such as "g", "p", "y", "q", etc. * * The font height in CADSTAR corresponds to the full 24 point height. In KiCad it only * corresponds to the height above the guide line, meaning the overall text height will be * larger in KiCad. */ static const double TXT_HEIGHT_RATIO; /** * These are special fields in text objects enclosed between the tokens '<@' and '@>' such as * <@[FIELD_NAME][FIELD_VALUE]@>. For example: "<@DESIGN TITLEProject Title@>" */ enum class TEXT_FIELD_NAME { DESIGN_TITLE, SHORT_JOBNAME, LONG_JOBNAME, NUM_OF_SHEETS, SHEET_NUMBER, SHEET_NAME, VARIANT_NAME, VARIANT_DESCRIPTION, REG_USER, COMPANY_NAME, CURRENT_USER, DATE, TIME, MACHINE_NAME, FROM_FILE, DISTANCE, UNITS_SHORT, UNITS_ABBREV, UNITS_FULL, HYPERLINK, NONE ///< Synthetic for flagging }; /** * Map between CADSTAR fields and KiCad text variables. This is used as a lookup table when * parsing CADSTAR text fields. Most variables have a similar name in KiCad as in CADSTAR. */ static const std::map CADSTAR_TO_KICAD_FIELDS; struct PARSER_CONTEXT { /** * CADSTAR doesn't have user defined text fields but does allow loading text from a * file. This map stores all the text items that exist in the original CADSTAR design. They * will be replaced by a text variable of the form ${FROM_FILE_[filename]_[extension]} */ std::map FilenamesToTextMap; /** * KiCad doesn't support hyperlinks but we use this map to display warning messages * after import. First element = Display Text. Second element = Hyperlink */ std::map TextToHyperlinksMap; /** * Values for the text field elements used in the CADSTAR design extracted from the * text element instances */ std::map TextFieldToValuesMap; /** * Text fields need to be updated in CADSTAR and it is possible that they are not * consistent across text elements. */ std::set InconsistentTextFields; /** * Callback function to report progress */ std::function CheckPointCallback = []() {}; }; /** * @brief Replaces CADSTAR fields for the equivalent in KiCad and stores the field values * in aParserContext * @param aTextString Text string to parse * @param aParserContext PARSER_CONTEXT in which to store the values of the found fields * @return */ static wxString ParseTextFields( const wxString& aTextString, PARSER_CONTEXT* aParserContext ); struct PARSER { virtual void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) = 0; virtual ~PARSER() {}; }; struct FORMAT : PARSER { wxString Type; long SomeInt; ///< It is unclear what this parameter is used for long Version; ///< Archive version number (e.g. for PCB: 19=> CADSTAR 17.0 archive, ///< 20=> CADSTAR 18.0 archive, 21 => CADSTAR 2018.0 / 2019.0 / 2020.0, ///< etc.) void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct TIMESTAMP : PARSER { long Year; long Month; long Day; long Hour; long Minute; long Second; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; //Note: there are possibly several other resolutions, but HUNDREDTH MICRON is only one known enum class RESOLUTION { HUNDREDTH_MICRON }; struct HEADER : PARSER { FORMAT Format; wxString JobFile; wxString JobTitle; wxString Generator; RESOLUTION Resolution; TIMESTAMP Timestamp; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct VARIANT : PARSER ///< Nodename = "VARIANT" or "VMASTER" (master variant { VARIANT_ID ID = wxEmptyString; VARIANT_ID ParentID = wxEmptyString; ///< if empty, then this one is the master wxString Name = wxEmptyString; wxString Description = wxEmptyString; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct VARIANT_HIERARCHY : PARSER { std::map Variants; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; enum class LINESTYLE { SOLID, DASH, DASHDOT, DASHDOTDOT, DOT }; struct LINECODE : PARSER { LINECODE_ID ID; wxString Name; long Width; LINESTYLE Style; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct HATCH : PARSER { long Step; long LineWidth; long OrientAngle; ///< 1/1000 of a Degree void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct HATCHCODE : PARSER { HATCHCODE_ID ID; wxString Name; std::vector Hatches; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; static const long FONT_NORMAL = 400; static const long FONT_BOLD = 700; struct FONT : PARSER { wxString Name = wxT( "CADSTAR" ); long Modifier1 = FONT_NORMAL; ///< It seems this is related to weight. 400=Normal, 700=Bold. long Modifier2 = 0; ///< It seems this is always 0 regardless of settings bool KerningPairs = false; ///< From CADSTAR Help: "Kerning Pairs is for causing the system to ///< automatically reduce the spacing between certain pairs of ///< characters in order to improve the appearance of the text" bool Italic = false; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct TEXTCODE : PARSER { TEXTCODE_ID ID; wxString Name; long LineWidth; long Height; long Width; ///< Defaults to 0 if using system fonts or, if using CADSTAR font, default to ///< equal height (1:1 aspect ratio). Allows for system fonts to be rendered in ///< a different aspect ratio. FONT Font; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct ROUTEREASSIGN : PARSER { LAYER_ID LayerID; long OptimalWidth; long MinWidth; long MaxWidth; long NeckedWidth; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct ROUTECODE : PARSER { ROUTECODE_ID ID; wxString Name; long OptimalWidth; long MinWidth; long MaxWidth; long NeckedWidth; std::vector RouteReassigns; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /** * @brief Represents a floating value in E notation */ struct EVALUE : PARSER { long Base = 0; long Exponent = 0; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; double GetDouble(); }; /** * @brief Represents a point in x,y coordinates */ struct POINT : wxPoint, PARSER { POINT() : wxPoint( UNDEFINED_VALUE, UNDEFINED_VALUE ) {} POINT( int aX, int aY ) : wxPoint( aX, aY ) {} void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct LONGPOINT : PARSER { long x = UNDEFINED_VALUE; long y = UNDEFINED_VALUE; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct TRANSFORM { virtual wxPoint Apply( POINT aCadstarPoint ) = 0; }; enum class VERTEX_TYPE { POINT, CLOCKWISE_ARC, CLOCKWISE_SEMICIRCLE, ANTICLOCKWISE_ARC, ANTICLOCKWISE_SEMICIRCLE }; /** * @brief Represents a vertex in a shape. E.g. A circle is made by two semicircles with the same * center point. */ struct VERTEX : PARSER { VERTEX( VERTEX_TYPE aType = VERTEX_TYPE::POINT, POINT aEnd = POINT(), POINT aCenter = POINT() ) : Type( aType ), End( aEnd ), Center( aCenter ) {} VERTEX_TYPE Type; POINT End; POINT Center; static bool IsVertex( XNODE* aNode ); void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; void AppendToChain( SHAPE_LINE_CHAIN* aChainToAppendTo, const std::function aCadstarToKicadPointCallback, double aAccuracy ) const; }; /** * @brief Represents a cutout in a closed shape (e.g. OUTLINE) */ struct CUTOUT : PARSER { std::vector Vertices; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; enum class SHAPE_TYPE { OPENSHAPE, ///< Unfilled open shape. Cannot have cutouts. OUTLINE, ///< Unfilled closed shape. SOLID, ///< Filled closed shape (solid fill). HATCHED ///< Filled closed shape (hatch fill). }; struct SHAPE : PARSER { SHAPE_TYPE Type; std::vector Vertices; std::vector Cutouts; ///< Not Applicable to OPENSHAPE Type wxString HatchCodeID; ///< Only Applicable for HATCHED Type static bool IsShape( XNODE* aNode ); void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; SHAPE_LINE_CHAIN OutlineAsChain( const std::function aCadstarToKicadPointCallback, double aAccuracy ) const; SHAPE_POLY_SET ConvertToPolySet( const std::function aCadstarToKicadPointCallback, double aAccuracy ) const; }; enum class UNITS { DESIGN, ///< Inherits from design units (assumed Assignments->Technology->Units) THOU, INCH, MICROMETRE, MM, CENTIMETER, METER }; static UNITS ParseUnits( XNODE* aNode ); enum class ANGUNITS { DEGREES, RADIANS }; static ANGUNITS ParseAngunits( XNODE* aNode ); enum class GRID_TYPE { FRACTIONALGRID, ///< Param1 = Units, Param2 = Divisor. The grid is equal in X and Y ///< dimensions with a step size equal to Param1/Param2 STEPGRID ///< Param1 = X Step, Param2 = Y Step. A standard x,y grid. }; struct GRID : PARSER { GRID_TYPE Type; wxString Name; long Param1; ///< Either Units or X step, depending on Type (see GRID_TYPE for ///< more details) long Param2; ///< Either Divisor or Y step, depending on Type (see GRID_TYPE for ///< more details) static bool IsGrid( XNODE* aNode ); void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct GRIDS : PARSER { GRID WorkingGrid; GRID ScreenGrid; ///< From CADSTAR Help: "There is one Screen Grid, which is ///< visible as dots on the screen. You cannot specify your ///< own name for the Screen Grid. The visibility and colour ///< of the dots representing the Screen Grid is set up by ///< the Highlight category on the Colours dialog. ///< ///< The type of Screen grid displayed(Lined or Points) is ///< set up on the Display dialog within Options(File menu)." std::vector UserGrids; ///< List of predefined grids created by the user void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct SETTINGS : PARSER { UNITS Units; ///< Units to display for linear dimensions long UnitDisplPrecision; ///< Number of decimal points to display for linear dimensions long InterlineGap; ///< For CADSTAR font only, distance between lines of text, ///< expressed as a percentage of the text height (accepted ///< values are 0-100) long BarlineGap; ///< For CADSTAR font only, distance between top bar and ///< character, expressed as a percentage of the text height ///< (accepted values are 0-100) bool AllowBarredText = false; ///< Specifies if barring is allowed in the design long AngularPrecision; ///< Number of decimal points to display for angular dimensions LONGPOINT DesignOrigin; std::pair DesignArea; LONGPOINT DesignRef; ///< Appears to be 0,0 always LONGPOINT DesignLimit; bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); virtual void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /** * @brief From CADSTAR Help: "Text Alignment enables you to define the position of an alignment * origin for all text items in CADSTAR. The alignment origin is a point on or within the text * boundary and defines how the text is displayed. * * For example, with an alignment of bottom-right the origin will be positioned at the bottom * right of the text boundary. This makes it easier to right-align several text items * regardless of the length of text displayed. * * Text Alignment applies to all CADSTAR text. [...] * * Note: Unaligned text operates in the way CADSTAR text always has. In most cases this behaves * as Bottom Left alignment, but there are a few exceptions, e.g. pin names. Also unaligned * multiline text has an origin Bottom Left of the first line." * * See also JUSTIFICATION */ enum class ALIGNMENT { NO_ALIGNMENT, ///< NO_ALIGNMENT has different meaning depending on the object type TOPLEFT, TOPCENTER, TOPRIGHT, CENTERLEFT, CENTERCENTER, CENTERRIGHT, BOTTOMLEFT, BOTTOMCENTER, BOTTOMRIGHT }; static ALIGNMENT ParseAlignment( XNODE* aNode ); /** * @brief From CADSTAR Help: "Multi Line Text can also be justified as Left, Centre or Right. * This does not affect the text alignment. Note: Justification of single line text has no * effect." * * This only affects multiline text * * See also ALIGNMENT */ enum class JUSTIFICATION { LEFT, CENTER, RIGHT }; static JUSTIFICATION ParseJustification( XNODE* aNode ); /** * @brief Sets the readability direction of text. From CADSTAR Help: "Horizontal text will * always be displayed from left to right (i.e. never upside down). Vertical text can be set as * readable from either the left or right edge of the design." * * I.e. Vertical text can either be rotated 90 degrees clockwise or 90 degrees anticlockwise from * horizontal. This does not impact vertical text */ enum class READABILITY { BOTTOM_TO_TOP, ///< When text is vertical, show it rotated 90 degrees anticlockwise TOP_TO_BOTTOM ///< When text is vertical, show it rotated 90 degrees clockwise }; static READABILITY ParseReadability( XNODE* aNode ); enum class ATTROWNER { ALL_ITEMS, AREA, BOARD, COMPONENT, CONNECTION, COPPER, DOCSYMBOL, FIGURE, NET, NETCLASS, PART, ///< Only library Attributes PART_DEFINITION, ///< Only library Attributes PIN, SIGNALREF, SYMBOL, SYMDEF, TEMPLATE, TESTPOINT }; enum class ATTRUSAGE { BOTH, ///< From CADSTAR Help: Assigned to both Schematic symbols and PCB components, ///< and displayed on Schematic and PCB designs. COMPONENT, ///< From CADSTAR Help: Assigned to PCB components and displayed on PCB designs PART_DEFINITION, ///< From CADSTAR Help: Assigned to Parts library Definitions and displayed ///< by the Library searcher PART_LIBRARY, ///< From CADSTAR Help: Only used by non-Cadstar applications SYMBOL, ///< From CADSTAR Help: Assigned to Schematic Symbols and displayed on ///< Schematic Designs UNDEFINED ///< Note: It seems that some attribute have no "ATTRUSAGE" defined. It appears ///< that the attributes that fall under this category are the ones associated ///< with the design itself (i.e. not inherited from the library) }; struct ATTRIBUTE_LOCATION : PARSER { TEXTCODE_ID TextCodeID; LAYER_ID LayerID; POINT Position; long OrientAngle = 0; bool Mirror = false; bool Fixed = false; JUSTIFICATION Justification = JUSTIFICATION::LEFT; ///< Note: Justification has no effect on single lines of text ALIGNMENT Alignment = ALIGNMENT:: NO_ALIGNMENT; ///< In CADSTAR The default alignment for a TEXT object (when ///< "(No Alignment()" is selected) Bottom Left of the *first line*. ///< Note that this is different from BOTTOM_LEFT (which is bottom ///< left of the whole text block) void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext ); bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); virtual void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /** * @brief NOTE from CADSTAR help: To convert a Part Definition Attribute into a hyperlink, prefix * the attribute name with "Link " */ struct ATTRNAME : PARSER { struct COLUMNORDER : PARSER { long ID; long Order; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct COLUMNWIDTH : PARSER { long ID; long Width; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; ATTRIBUTE_ID ID; wxString Name; ///< Parenthesis aren't permitted in user attributes in CADSTAR. Any ///< Attributes in Parenthesis indicate an internal CADSTAR attribute ///< Examples: "(PartDescription)" "(PartDefinitionNameStem)",etc. ///TODO: create a list of all CADSTAR internal attribute names. ATTROWNER AttributeOwner = ATTROWNER::ALL_ITEMS; ATTRUSAGE AttributeUsage = ATTRUSAGE::UNDEFINED; bool NoTransfer = false; ///< True="All Design Types", False="Current Design Type" ///< "All Design Types" Description from CADSTAR Help: ///< "The selected attribute name will be available when ///< any design is displayed" ///< "Current Design Type" From CADSTAR Help: This ///< restricts the availability of the selected attribute ///< name to the current design. std::vector ColumnOrders; std::vector ColumnWidths; bool ColumnInvisible = false; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct ATTRIBUTE_VALUE : PARSER { ATTRIBUTE_ID AttributeID; wxString Value; bool ReadOnly = false; bool HasLocation = false; ///< Flag to know if this ATTRIBUTE_VALUE has a location ///< i.e. is displayed ATTRIBUTE_LOCATION AttributeLocation; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /** * @brief Corresponds to CADSTAR "origin". This is used for setting a location of an attribute * e.g. Designator (called Component Name in CADSTAR), Part Name (name of component in the * library), etc. The atom identifier is "TEXTLOC" */ struct TEXT_LOCATION : ATTRIBUTE_LOCATION { TEXT_LOCATION() { // The default alignment for TEXT_LOCATION (when "NO_ALIGNMENT" is selected) is // Bottom left, matching CADSTAR's default behaviour Alignment = ALIGNMENT::BOTTOMLEFT; } ATTRIBUTE_ID AttributeID; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct CADSTAR_NETCLASS : PARSER { NETCLASS_ID ID; wxString Name; std::vector Attributes; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct SPCCLASSNAME : PARSER { SPACING_CLASS_ID ID; wxString Name; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct CODEDEFS : PARSER { std::map LineCodes; std::map HatchCodes; std::map TextCodes; std::map RouteCodes; std::map AttributeNames; std::map NetClasses; std::map SpacingClassNames; bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); }; /** * @brief Corresponds to "Display when" Item property. From CADSTAR * Help: "This parameter enables you to make the visibility of * a component outline/area (or an area of component copper, or * a string of component text) dependent on the current mirror * status of the component. * * For example, you may require a string of component text to * be displayed only when the component is mirrored." */ enum class SWAP_RULE { NO_SWAP, ///< Display when Unmirrored USE_SWAP_LAYER, ///< Display when Mirrored BOTH ///< Always display (Mirrored and Unmirrored) }; static SWAP_RULE ParseSwapRule( XNODE* aNode ); struct REUSEBLOCK : PARSER { REUSEBLOCK_ID ID; wxString Name; wxString FileName; ///< Filename of the reuse block (usually a .pcb). Used for reloading bool Mirror = false; long OrientAngle = 0; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /** * @brief References an element from a design reuse block */ struct REUSEBLOCKREF : PARSER { REUSEBLOCK_ID ReuseBlockID = wxEmptyString; wxString ItemReference = wxEmptyString; ///< For Components, this references the designator ///< in the reuse file. ///< For net elements (such as vias, routes, etc.), ///< coppers and templates, this parameter is blank. bool IsEmpty(); ///< Determines if this is empty (i.e. no design reuse associated) void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct GROUP : PARSER { GROUP_ID ID; wxString Name; bool Fixed = false; bool Transfer = false; ///< If true, the group is transferred to PCB GROUP_ID GroupID = wxEmptyString; ///< If not empty, this GROUP ///< is part of another GROUP REUSEBLOCKREF ReuseBlockRef; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct FIGURE : PARSER { FIGURE_ID ID; LINECODE_ID LineCodeID; LAYER_ID LayerID; SHAPE Shape; //< Uses the component's coordinate frame if within a component //< definition, otherwise uses the design's coordinate frame. GROUP_ID GroupID = wxEmptyString; ///< If not empty, this FIGURE is part of a group REUSEBLOCKREF ReuseBlockRef; SWAP_RULE SwapRule = SWAP_RULE::BOTH; ///< Only applicable to Figures in Components bool Fixed = false; std::map AttributeValues; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct TEXT : PARSER { TEXT_ID ID; wxString Text; //TODO: Need to lex/parse to identify design fields and hyperlinks TEXTCODE_ID TextCodeID; LAYER_ID LayerID; POINT Position; long OrientAngle = 0; bool Mirror = false; bool Fixed = false; SWAP_RULE SwapRule = SWAP_RULE::BOTH; JUSTIFICATION Justification = JUSTIFICATION::LEFT; ///< Note: Justification has no effect on single lines of text ALIGNMENT Alignment = ALIGNMENT:: NO_ALIGNMENT; ///< In CADSTAR The default alignment for a TEXT object (when ///< "(No Alignment()" is selected) Bottom Left of the *first line*. ///< Note that this is different from BOTTOM_LEFT (which is bottom ///< left of the whole text block) GROUP_ID GroupID = wxEmptyString; ///< If not empty, this FIGURE is part of a group REUSEBLOCKREF ReuseBlockRef; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext, bool aParseFields ); }; struct SYMDEF : PARSER { SYMDEF_ID ID; wxString ReferenceName; ///< This is the name which identifies the symbol in the library ///< Multiple components may exist with the same ReferenceName. wxString Alternate; ///< This is in addition to ReferenceName. It allows defining ///< different versions, views etc. of the same basic symbol. POINT Origin; ///< Origin of the component (this is used as the reference point ///< when placing the component in the design) bool Stub = false; ///< When the CADSTAR Archive file is exported without the ///< component library, if components on the board are still ///< exported, the Reference and Alternate names will still be ///< exported but the content is replaced with a "STUB" atom, ///< requiring access to the original library for component ///< definition. long Version = UNDEFINED_VALUE; ///< Version is a sequential integer number to identify ///< discrepancies between the library and the design. std::map Figures; std::map Texts; std::map TextLocations; ///< This contains location of ///< any attributes, including ///< designator position std::map AttributeValues; ///< These attributes might also ///< have a location void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext ); bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); }; struct PART : PARSER { enum class PIN_TYPE { UNCOMMITTED, ///< Uncommitted pin (default) INPUT, ///< Input pin OUTPUT_OR, ///< Output pin OR tieable OUTPUT_NOT_OR, ///< Output pin not OR tieable OUTPUT_NOT_NORM_OR, ///< Output pin not normally OR tieable POWER, ///< Power pin GROUND, ///< Ground pin TRISTATE_BIDIR, ///< Tristate bi-directional driver pin TRISTATE_INPUT, ///< Tristate input pin TRISTATE_DRIVER ///< Tristate output pin }; static PIN_TYPE GetPinType( XNODE* aNode ); struct DEFINITION : PARSER ///< "PARTDEFINITION" node name { struct GATE : PARSER ///< "GATEDEFINITION" node name { GATE_ID ID; ///< Usually "A", "B", "C", etc. wxString Name; ///< Symbol name in the symbol library wxString Alternate; ///< Symbol alternate name in the symbol library long PinCount; ///< Number of pins (terminals) in the symbol void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct PIN : PARSER ///< "PARTDEFINITIONPIN" node name { /** * @brief Positioning of pin names can be in one of four quadrants */ enum class POSITION { TOP_RIGHT = 0, ///< Default TOP_LEFT = 1, BOTTOM_LEFT = 2, BOTTOM_RIGHT = 3 }; PART_DEFINITION_PIN_ID ID = UNDEFINED_VALUE; wxString Identifier = wxEmptyString; ///< This should match a pad identifier ///< in the component footprint ///< subnode="PINIDENTIFIER". It is assumed ///< that this could be empty in earlier ///< versions of CADSTAR wxString Name = wxEmptyString; ///< Can be empty. If empty the pin name ///< displayed will be Identifier ///< (subnode="PINNAME") ///< This seems to be equivalent to "Pin ///< Number" in KiCad. wxString Label = wxEmptyString; ///< This Can be empty (subnode= ///< "PINLABEL") ///< From CADSTAR Help: "Pin ///< Labels are an optional replacement ///< for the free text sometimes placed ///< in schematic symbols. Using Pin ///< Labels instead has the advantage of ///< associating each piece of label ///< text with a particular pin. This ///< means that the text remains ///< correctly placed after any Gate and ///< Pin Swaps are Back Annotated to the ///< Schematic design." ///< This seems to be equivalent to "Pin ///< Name" in KiCad. wxString Signal = wxEmptyString; ///< Usually for Power/Ground pins, ///< (subnode="PINSIGNAL") GATE_ID TerminalGate; ///< (subnode="PINTERM", param0) TERMINAL_ID TerminalPin; ///< (subnode="PINTERM", param1) PIN_TYPE Type = PIN_TYPE::UNCOMMITTED; ///< subnode="PINTYPE" long Load = UNDEFINED_VALUE; ///< The electrical current expected on ///< the pin (It is unclear what the units ///< are, but only accepted values are ///< integers) subnode ="PINLOAD" POSITION Position = POSITION::TOP_RIGHT; ///< The pin names will use these positions when ///< the symbol is added to a schematic design ///< subnode="PINPOSITION" void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct PIN_EQUIVALENCE : PARSER ///< "PINEQUIVALENCE" Node name { std::vector PinIDs; ///< All the pins in this vector are ///< equivalent and can be swapped with ///< each other void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct SWAP_GATE : PARSER ///< "SWAPGATE" Node name (represents an "Element") { std::vector PinIDs; ///< The pins in this vector ///< describe a "gate" void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct SWAP_GROUP : PARSER { wxString GateName = wxEmptyString; ///< Optional. If not empty, should match the Name ///< attribute of one of the gates defined in the ///< part definition bool External = false; ///< Determines if this swap group is external (and internal) ///< or internal only. External Gate swapping allows Gates on ///< different components with the same Part Definition to ///< swap with one another. ///< ///< The external swapping groups must be at the root level ///< (i.e. they cannot be contained by other swapping groups) std::vector SwapGates; ///< Each of the elements in this vector can be ///< swapped with each other - i.e. *all* of the ///< pins in each swap gate can be swapped with ///< *all* in another swap gate defined in this ///< vector void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; wxString Name; ///< This name can be different to the PART name bool HidePinNames = false; ///< Specifies whether to display the pin names/identifier in ///< the schematic symbol or not. E.g. it is useful to display ///< pin name information for integrated circuits but less so ///< for resistors and capacitors. (subnode HIDEPINNAMES) long MaxPinCount = UNDEFINED_VALUE; ///< Optional parameter which is used for specifying the number ///< of electrical pins on the PCB component symbol to be ///< used in the part definition (this should not include ///< mechanical pins for fixing etc.). This value must be less ///< than or equal to than the number of pins on the PCB ///< Component symbol. std::map GateSymbols; std::map Pins; std::map AttributeValues; ///< Some attributes are ///< defined within the part ///< definition, whilst others ///< are defined in the part std::vector PinEquivalences; std::vector SwapGroups; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct PART_PIN : PARSER ///< "PARTPIN" node name { PART_PIN_ID ID; wxString Name = wxEmptyString; PIN_TYPE Type = PIN_TYPE::UNCOMMITTED; wxString Identifier = wxEmptyString; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; PART_ID ID; wxString Name; long Version; DEFINITION Definition; std::map PartPins; ///< It is unclear why there are two "Pin" ///< structures in CPA files... PART_PIN seems to ///< be a reduced version of PART::DEFINITION::PIN ///< Therefore, PART_PIN is only included for ///< completeness of the parser, but won't be used bool HidePinNames = false; ///< This seems to be a duplicate of DEFINITION::HidePinNames ///< Possibly only used in older file formats? std::map AttributeValues; ///< Some attributes are defined ///< within the part definition, ///< whilst others are defined in ///< the part itself void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct PARTS : PARSER { std::map PartDefinitions; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct NET : PARSER { struct JUNCTION : PARSER ///< "JPT" nodename. { NETELEMENT_ID ID; ///< First character is "J" LAYER_ID LayerID; POINT Location; GROUP_ID GroupID = wxEmptyString; ///< If not empty, this JUCTION is part of a ///< group REUSEBLOCKREF ReuseBlockRef; bool Fixed = false; void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext ); bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); virtual void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct CONNECTION : PARSER ///< "CONN" nodename { NETELEMENT_ID StartNode; NETELEMENT_ID EndNode; ROUTECODE_ID RouteCodeID; bool Fixed = false; bool Hidden = false; GROUP_ID GroupID = wxEmptyString; ///< If not empty, this connection is part of a group REUSEBLOCKREF ReuseBlockRef; std::map AttributeValues; ///< It is possible to add ///< attributes solely to a ///< particular connection void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext ); bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); virtual ~CONNECTION() {} }; NET_ID ID; ROUTECODE_ID RouteCodeID = wxEmptyString; ///< "NETCODE" subnode long SignalNum = UNDEFINED_VALUE; ///< This is undefined if the net has been ///< given a name. "SIGNUM" subnode. wxString Name = wxEmptyString; ///< This is undefined (wxEmptyString) if the net ///< is unnamed. "SIGNAME" subnode bool Highlight = false; std::map Junctions; std::map AttributeValues; NETCLASS_ID NetClassID = wxEmptyString; ///< The net might not have a net class, in which case it will be ///< wxEmptyString ("NETCLASSREF" subnode) SPACING_CLASS_ID SpacingClassID = wxEmptyString; ///< The net might not have a spacing class, in which case it will ///< be wxEmptyString ("SPACINGCLASS" subnode) void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext ); bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext ); }; struct DOCUMENTATION_SYMBOL : PARSER { DOCUMENTATION_SYMBOL_ID ID; SYMDEF_ID SymdefID; ///< Normally documentation symbols only have TEXT, FIGURE and ///< TEXT_LOCATION objects which are all drawn on the "(Undefined)" ///< Layer. When used in the design, the user has to specify which ///< layer to draw it on. LAYER_ID LayerID; ///< Move all objects in the Symdef to this layer. POINT Origin; ///< Origin of the component (this is used as the reference point ///< when placing the component in the design) GROUP_ID GroupID = wxEmptyString; ///< If not empty, this component is part of a group REUSEBLOCKREF ReuseBlockRef; long OrientAngle = 0; bool Mirror = false; bool Fixed = false; READABILITY Readability = READABILITY::BOTTOM_TO_TOP; long ScaleRatioNumerator = 1; ///< Documentation symbols can be arbitrarily scaled when ///< added to a design long ScaleRatioDenominator = 1; ///< Documentation symbols can be arbitrarily scaled when ///< added to a design std::map AttributeValues; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct DFLTSETTINGS : PARSER { COLOR_ID Color; bool IsVisible = true; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct ATTRCOL : PARSER { ATTRIBUTE_ID AttributeID; COLOR_ID Color; bool IsVisible = true; bool IsPickable = true; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct ATTRCOLORS : PARSER { DFLTSETTINGS DefaultSettings; std::map AttributeColors; bool IsVisible = true; // unclear what this represents - maybe all attributes are hidden? void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; struct PARTNAMECOL : PARSER { COLOR_ID Color; bool IsVisible = true; bool IsPickable = true; void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override; }; /////////////////////// // HELPER FUNCTIONS: // /////////////////////// static void InsertAttributeAtEnd( XNODE* aNode, wxString aValue ); /** * @brief Reads a CADSTAR Archive file (S-parameter format) * @param aFileName * @param aFileTypeIdentifier Identifier of the first node in the file to check against. E.g. "CADSTARPCB" * @param aProgressReporter Pointer to a Progress Reporter to report progress to. * @return XNODE pointing to the top of the tree for further parsing. Each node has the first * element as the node's name and subsequent elements as node attributes ("attr0", * "attr1", "attr2", etc.). Caller is responsible for deleting to avoid memory leaks. * @throws IO_ERROR */ static XNODE* LoadArchiveFile( const wxString& aFileName, const wxString& aFileTypeIdentifier, PROGRESS_REPORTER* aProgressReporter = nullptr ); /** * @brief * @param aAttribute * @return */ static bool IsValidAttribute( wxXmlAttribute* aAttribute ); /** * @brief * @param aNode * @param aID * @param aIsRequired Prevents exception throwing if false. * @return returns the value (wxString) of attribute "attrX" in aNode where 'X' is aID * @throws IO_ERROR if attribute does not exist */ static wxString GetXmlAttributeIDString( XNODE* aNode, unsigned int aID, bool aIsRequired = true ); /** * @brief * @param aNode * @param aID * @param aIsRequired Prevents exception throwing if false. * @return returns the value (long) of attribute "attrX" in aNode where 'X' is aID * @throws IO_ERROR if attribute does not exist */ static long GetXmlAttributeIDLong( XNODE* aNode, unsigned int aID, bool aIsRequired = true ); /** * @brief * @param aNode * @throw IO_ERROR if a child node was found */ static void CheckNoChildNodes( XNODE* aNode ); /** * @brief * @param aNode * @throw IO_ERROR if a node adjacent to aNode was found */ static void CheckNoNextNodes( XNODE* aNode ); /** * @brief * @param aNode with a child node containing an EVALUE * @param aValueToParse * @throw IO_ERROR if unable to parse or node is not an EVALUE */ static void ParseChildEValue( XNODE* aNode, PARSER_CONTEXT* aContext, EVALUE& aValueToParse ); /** * @brief if no children are present, it just returns an empty * vector (without throwing an exception) * @param aNode containing a series of POINT objects * @param aTestAllChildNodes * @param aExpectedNumPoints if UNDEFINED_VALUE (i.e. -1), this is check is disabled * @return std::vector containing all POINT objects * @throw IO_ERROR if one of the following: * - Unable to parse a POINT object * - aTestAllChildNodes is true and one of the child nodes is not a valid POINT object * - aExpectedNumPoints is non-negative and the number of POINT objects found is different */ static std::vector ParseAllChildPoints( XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes = false, int aExpectedNumPoints = UNDEFINED_VALUE ); /** * @brief if no children are present, it just returns an empty * vector (without throwing an exception) * @param aNode containing a series of VERTEX objects * @param aTestAllChildNodes * @param aExpectedNumPoints if -1, this is check is disabled * @return std::vector containing all VERTEX objects * @throw IO_ERROR if one of the following: * - Unable to parse a VERTEX object * - aTestAllChildNodes is true and one of the child nodes is not a valid VERTEX object */ static std::vector ParseAllChildVertices( XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes = false ); /** * @brief if no children are present, it just returns an empty * vector (without throwing an exception) * @param aNode containing a series of CUTOUT objects * @param aTestAllChildNodes * @param aExpectedNumPoints if -1, this is check is disabled * @return std::vector containing all CUTOUT objects * @throw IO_ERROR if one of the following: * - Unable to parse a CUTOUT object * - aTestAllChildNodes is true and one of the child nodes is not a valid CUTOUT object */ static std::vector ParseAllChildCutouts( XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes = false ); static long GetNumberOfChildNodes( XNODE* aNode ); static long GetNumberOfStepsForReporting( XNODE* aRootNode, std::vector aSubNodeChildrenToCount ); /** * @brief Convert a string with CADSTAR overbar characters to equivalent in KiCad * @param aCadstarString Input string * @return KiCad string with overbar characters */ static wxString HandleTextOverbar( wxString aCadstarString ); /** * Corrects the position of a text element that had NO_ALIGNMENT in CADSTAR. Assumes that the * provided text element has been initialised with a position and orientation. * @param aKiCadTextItem a Kicad item to correct */ static void FixTextPositionNoAlignment( EDA_TEXT* aKiCadTextItem ); static wxString generateLibName( const wxString& aRefName, const wxString& aAlternateName ) { return aRefName + ( ( aAlternateName.size() > 0 ) ? ( wxT( " (" ) + aAlternateName + wxT( ")" ) ) : wxT( "" ) ); } protected: void checkPoint(); ///< Updates m_progressReporter or throws if user cancelled PARSER_CONTEXT m_context; PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr }; // class CADSTAR_ARCHIVE_PARSER #endif // CADSTAR_ARCHIVE_PARSER_H_