kicad/common/plugins/cadstar/cadstar_archive_parser.h

1441 lines
55 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020-2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
* 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 <http://www.gnu.org/licenses/>.
*/
/**
* @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 <richio.h>
#include <wx/string.h>
#include <functional>
#include <map>
#include <set>
#include <vector>
#include <xnode.h>
#include <math/vector2d.h>
#include <plugins/cadstar/cadstar_archive_objects.h>
// 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;
class SHAPE_ARC;
/**
* @brief Helper functions and common structures for CADSTAR PCB and Schematic archive files.
*/
class CADSTAR_ARCHIVE_PARSER
{
public:
CADSTAR_ARCHIVE_PARSER() { m_progressReporter = nullptr; }
virtual ~CADSTAR_ARCHIVE_PARSER() {}
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<TEXT_FIELD_NAME, wxString> 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<wxString, wxString> 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<wxString, wxString> TextToHyperlinksMap;
/**
* Values for the text field elements used in the CADSTAR design extracted from the
* text element instances
*/
std::map<TEXT_FIELD_NAME, wxString> TextFieldToValuesMap;
/**
* Text fields need to be updated in CADSTAR and it is possible that they are not
* consistent across text elements.
*/
std::set<TEXT_FIELD_NAME> InconsistentTextFields;
/**
* Callback function to report progress
*/
std::function<void()> 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<VARIANT_ID, VARIANT> 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<HATCH> 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<ROUTEREASSIGN> 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 : VECTOR2I, PARSER
{
POINT() : VECTOR2I( UNDEFINED_VALUE, UNDEFINED_VALUE ) {}
POINT( int aX, int aY ) : VECTOR2I( 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 VECTOR2I 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<VECTOR2I( const VECTOR2I& )> aCadstarToKicadPointCallback,
double aAccuracy ) const;
SHAPE_ARC BuildArc( const VECTOR2I& aPrevPoint,
const std::function<VECTOR2I( const VECTOR2I& )>
aCadstarToKicadPointCallback ) const;
};
/**
* @brief Represents a cutout in a closed shape (e.g. OUTLINE)
*/
struct CUTOUT : PARSER
{
std::vector<VERTEX> 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<VERTEX> Vertices;
std::vector<CUTOUT> 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<VECTOR2I( const VECTOR2I& )>
aCadstarToKicadPointCallback,
double aAccuracy ) const;
SHAPE_POLY_SET ConvertToPolySet( const std::function<VECTOR2I( const VECTOR2I& )>
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<GRID> 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
long PinNoOffset; ///< The distance, of a Pin Name/Identifier from its parent terminal.
long PinNoAngle; ///< The angle at which the Pin ID is positioned relative to a terminal.
LONGPOINT DesignOrigin;
std::pair<POINT, POINT> 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<COLUMNORDER> ColumnOrders;
std::vector<COLUMNWIDTH> 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<ATTRIBUTE_VALUE> 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<LINECODE_ID, LINECODE> LineCodes;
std::map<HATCHCODE_ID, HATCHCODE> HatchCodes;
std::map<TEXTCODE_ID, TEXTCODE> TextCodes;
std::map<ROUTECODE_ID, ROUTECODE> RouteCodes;
std::map<ATTRIBUTE_ID, ATTRNAME> AttributeNames;
std::map<NETCLASS_ID, CADSTAR_NETCLASS> NetClasses;
std::map<SPACING_CLASS_ID, SPCCLASSNAME> 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<ATTRIBUTE_ID, ATTRIBUTE_VALUE> 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<FIGURE_ID, FIGURE> Figures;
std::map<TEXT_ID, TEXT> Texts;
std::map<ATTRIBUTE_ID, TEXT_LOCATION> TextLocations; ///< This contains location of
///< any attributes, including
///< designator position
std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE> AttributeValues; ///< These attributes might also
///< have a location
wxString BuildLibName() const;
void ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext );
bool ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext );
};
struct PART : PARSER
{
static CADSTAR_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
{
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)
CADSTAR_PIN_TYPE Type = CADSTAR_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"
///
CADSTAR_PIN_POSITION Position =
CADSTAR_PIN_POSITION::TOP_RIGHT; ///< The pin names will use these positions
///< when the symbol is added to a design
///< subnode="PINPOSITION"
void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
};
struct PIN_EQUIVALENCE : PARSER ///< "PINEQUIVALENCE" Node name
{
std::vector<PART_DEFINITION_PIN_ID> 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<PART_DEFINITION_PIN_ID> 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<SWAP_GATE> 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<GATE_ID, GATE> GateSymbols;
std::map<PART_DEFINITION_PIN_ID, PIN> Pins;
std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE> AttributeValues; ///< Some attributes are
///< defined within the part
///< definition, whilst others
///< are defined in the part
std::vector<PIN_EQUIVALENCE> PinEquivalences;
std::vector<SWAP_GROUP> SwapGroups;
void Parse( XNODE* aNode, PARSER_CONTEXT* aContext ) override;
};
struct PART_PIN : PARSER ///< "PARTPIN" node name
{
PART_PIN_ID ID;
wxString Name = wxEmptyString;
CADSTAR_PIN_TYPE Type = CADSTAR_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<PART_PIN_ID, PART_PIN> 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<ATTRIBUTE_ID, ATTRIBUTE_VALUE> 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<PART_ID, PART> 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<ATTRIBUTE_ID, ATTRIBUTE_VALUE> 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<NETELEMENT_ID, JUNCTION> Junctions;
std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE> 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<ATTRIBUTE_ID, ATTRIBUTE_VALUE> 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<ATTRIBUTE_ID, ATTRCOL> 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<POINT> 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<VERTEX> 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<CUTOUT> ParseAllChildCutouts(
XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes = false );
static long GetNumberOfChildNodes( XNODE* aNode );
static long GetNumberOfStepsForReporting( XNODE* aRootNode,
std::vector<wxString> aSubNodeChildrenToCount );
static wxString EscapeFieldText( const wxString& aFieldText )
{
wxString ret = aFieldText;
ret.Replace( wxT( "\n" ), wxT( "\\n" ) );
ret.Replace( wxT( "\r" ), wxT( "\\r" ) );
ret.Replace( wxT( "\t" ), wxT( "\\t" ) );
return ret;
}
/**
* @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 );
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_