Add support for .gbrjob new file format (JSON format) in Gerbview
Add experimental code to generate .gbrjob files in the new JSON file format
This commit is contained in:
parent
1a845bc4ad
commit
2ff74cb3fc
|
@ -649,7 +649,6 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub
|
|||
EDA_DRAW_MODE_T trace_mode, void* aData )
|
||||
{
|
||||
wxASSERT( outputFile );
|
||||
int x0, y0, x1, y1, delta;
|
||||
wxSize size( aSize );
|
||||
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
|
||||
|
||||
|
@ -684,18 +683,17 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub
|
|||
if( trace_mode == FILLED )
|
||||
{
|
||||
// TODO: use an aperture macro to declare the rotated pad
|
||||
//
|
||||
|
||||
// Flash a pad anchor, if a netlist attribute is set
|
||||
if( aData )
|
||||
FlashPadCircle( pos, size.x, trace_mode, aData );
|
||||
|
||||
// The pad is reduced to an segment with dy > dx
|
||||
delta = size.y - size.x;
|
||||
x0 = 0;
|
||||
y0 = -delta / 2;
|
||||
x1 = 0;
|
||||
y1 = delta / 2;
|
||||
int delta = size.y - size.x;
|
||||
int x0 = 0;
|
||||
int y0 = -delta / 2;
|
||||
int x1 = 0;
|
||||
int y1 = delta / 2;
|
||||
RotatePoint( &x0, &y0, orient );
|
||||
RotatePoint( &x1, &y1, orient );
|
||||
GBR_METADATA metadata;
|
||||
|
@ -740,8 +738,7 @@ void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
|
|||
case 900:
|
||||
case 2700: // rotation of 90 degrees or 270 swaps sizes
|
||||
std::swap( size.x, size.y );
|
||||
|
||||
// Pass through
|
||||
// Pass through
|
||||
case 0:
|
||||
case 1800:
|
||||
if( trace_mode == SKETCH )
|
||||
|
@ -755,7 +752,7 @@ void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
|
|||
pos.y - (size.y - currentPenWidth) / 2 ),
|
||||
wxPoint( pos.x + (size.x - currentPenWidth) / 2,
|
||||
pos.y + (size.y - currentPenWidth) / 2 ),
|
||||
NO_FILL );
|
||||
NO_FILL, GetCurrentLineWidth() );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -800,24 +797,6 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS
|
|||
EDA_DRAW_MODE_T aTraceMode, void* aData )
|
||||
|
||||
{
|
||||
// Currently, a Pad RoundRect is plotted as polygon.
|
||||
// TODO: use Aperture macro and flash it
|
||||
SHAPE_POLY_SET outline;
|
||||
const int segmentToCircleCount = 64;
|
||||
TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient,
|
||||
aCornerRadius, segmentToCircleCount );
|
||||
|
||||
std::vector< wxPoint > cornerList;
|
||||
cornerList.reserve( segmentToCircleCount + 5 );
|
||||
// TransformRoundRectToPolygon creates only one convex polygon
|
||||
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
|
||||
|
||||
for( int ii = 0; ii < poly.PointCount(); ++ii )
|
||||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
|
||||
|
||||
// Close polygon
|
||||
cornerList.push_back( cornerList[0] );
|
||||
|
||||
GBR_METADATA gbr_metadata;
|
||||
|
||||
if( aData )
|
||||
|
@ -832,7 +811,32 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS
|
|||
gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
|
||||
}
|
||||
|
||||
PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
|
||||
if( aTraceMode != FILLED )
|
||||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
|
||||
|
||||
// Currently, a Pad RoundRect is plotted as polygon.
|
||||
// TODO: use Aperture macro and flash it
|
||||
SHAPE_POLY_SET outline;
|
||||
const int segmentToCircleCount = 64;
|
||||
TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient,
|
||||
aCornerRadius, segmentToCircleCount );
|
||||
|
||||
if( aTraceMode != FILLED )
|
||||
outline.Inflate( -GetCurrentLineWidth()/2, 16 );
|
||||
|
||||
std::vector< wxPoint > cornerList;
|
||||
// TransformRoundRectToPolygon creates only one convex polygon
|
||||
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
|
||||
cornerList.reserve( poly.PointCount() + 1 );
|
||||
|
||||
for( int ii = 0; ii < poly.PointCount(); ++ii )
|
||||
cornerList.push_back( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
|
||||
|
||||
// Close polygon
|
||||
cornerList.push_back( cornerList[0] );
|
||||
|
||||
PlotPoly( cornerList, aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL,
|
||||
aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata );
|
||||
|
||||
// Now, flash a pad anchor, if a netlist attribute is set
|
||||
// (remove me when a Aperture macro will be used)
|
||||
|
@ -854,7 +858,9 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize
|
|||
// However, because the anchor pad can be circle or rect, we use only
|
||||
// a circle not bigger than the rect.
|
||||
// the main purpose is to print a flashed DCode as pad anchor
|
||||
FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData );
|
||||
if( aTraceMode == FILLED )
|
||||
FlashPadCircle( aPadPos, std::min( aSize.x, aSize.y ), aTraceMode, aData );
|
||||
|
||||
GBR_METADATA gbr_metadata;
|
||||
|
||||
if( aData )
|
||||
|
@ -869,11 +875,20 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize
|
|||
gbr_metadata.m_NetlistMetadata.ClearAttribute( &attrname ); // not allowed on inner layers
|
||||
}
|
||||
|
||||
SHAPE_POLY_SET polyshape = *aPolygons;
|
||||
|
||||
if( aTraceMode != FILLED )
|
||||
{
|
||||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
|
||||
polyshape.Inflate( -GetCurrentLineWidth()/2, 16 );
|
||||
}
|
||||
|
||||
std::vector< wxPoint > cornerList;
|
||||
|
||||
for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
|
||||
for( int cnt = 0; cnt < polyshape.OutlineCount(); ++cnt )
|
||||
{
|
||||
SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
|
||||
SHAPE_LINE_CHAIN& poly = polyshape.Outline( cnt );
|
||||
|
||||
cornerList.clear();
|
||||
|
||||
for( int ii = 0; ii < poly.PointCount(); ++ii )
|
||||
|
@ -882,7 +897,9 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize
|
|||
// Close polygon
|
||||
cornerList.push_back( cornerList[0] );
|
||||
|
||||
PlotPoly( cornerList, ( aTraceMode == FILLED ) ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
|
||||
PlotPoly( cornerList,
|
||||
aTraceMode == FILLED ? FILLED_SHAPE : NO_FILL,
|
||||
aTraceMode == FILLED ? 0 : GetCurrentLineWidth(), &gbr_metadata );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -902,7 +919,7 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo
|
|||
|
||||
// Now, flash a pad anchor, if a netlist attribute is set
|
||||
// (remove me when a Aperture macro will be used)
|
||||
if( aData && (aTrace_Mode==FILLED) )
|
||||
if( aData && ( aTrace_Mode == FILLED ) )
|
||||
{
|
||||
// Calculate the radius of the circle inside the shape
|
||||
// It is the smaller dist from shape pos to edges
|
||||
|
@ -945,7 +962,9 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo
|
|||
}
|
||||
|
||||
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &metadata );
|
||||
PlotPoly( cornerList, aTrace_Mode==FILLED ? FILLED_SHAPE : NO_FILL, USE_DEFAULT_LINE_WIDTH, &metadata );
|
||||
PlotPoly( cornerList, aTrace_Mode == FILLED ? FILLED_SHAPE : NO_FILL,
|
||||
aTrace_Mode == FILLED ? 0 : GetCurrentLineWidth(),
|
||||
&metadata );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ set( GERBVIEW_SRCS
|
|||
gerbview_config.cpp
|
||||
gerbview_frame.cpp
|
||||
hotkeys.cpp
|
||||
json11.cpp
|
||||
job_file_reader.cpp
|
||||
locate.cpp
|
||||
menubar.cpp
|
||||
|
|
|
@ -41,19 +41,39 @@
|
|||
#include <html_messagebox.h>
|
||||
#include <view/view.h>
|
||||
|
||||
#include "json11.hpp" // A light JSON parser
|
||||
|
||||
/**
|
||||
* this class read and parse a Gerber job file to extract useful info
|
||||
* for GerbView
|
||||
*
|
||||
* In a gerber job file, data lines start by
|
||||
* In a gerber job file, old (deprecated) format, data lines start by
|
||||
* %TF. (usual Gerber X2 info)
|
||||
* %TJ.B. (board info)
|
||||
* %TJ.D. (design info)
|
||||
* %TJ.L. (layers info)
|
||||
* some others are not yet handled by Kicad
|
||||
* M02* is the last line
|
||||
|
||||
* In a gerber job file, JSON format, first lines are
|
||||
* {
|
||||
* "Header":
|
||||
* and the block ( a JSON array) containing the filename of files to load is
|
||||
* "FilesAttributes":
|
||||
* [
|
||||
* {
|
||||
* "Path": "interf_u-Composant.gbr",
|
||||
* "FileFunction": "Copper,L1,Top",
|
||||
* "FilePolarity": "Positive"
|
||||
* },
|
||||
* {
|
||||
* "Path": "interf_u-In1.Cu.gbr",
|
||||
* "FileFunction": "Copper,L2,Inr",
|
||||
* "FilePolarity": "Positive"
|
||||
* },
|
||||
* ],
|
||||
*/
|
||||
|
||||
class GERBER_JOBFILE_READER
|
||||
{
|
||||
public:
|
||||
|
@ -96,28 +116,65 @@ bool GERBER_JOBFILE_READER::ReadGerberJobFile()
|
|||
wxString msg;
|
||||
wxString data;
|
||||
|
||||
while( true )
|
||||
// detect the file format: old gerber format of new JSON format
|
||||
bool json_format = false;
|
||||
|
||||
char* line = jobfileReader.ReadLine();
|
||||
|
||||
if( !line ) // end of file
|
||||
return false;
|
||||
|
||||
data = line;
|
||||
|
||||
if( data.Contains("{" ) )
|
||||
json_format = true;
|
||||
|
||||
if( json_format )
|
||||
{
|
||||
char* line = jobfileReader.ReadLine();
|
||||
while( ( line = jobfileReader.ReadLine() ) )
|
||||
data << '\n' << line;
|
||||
|
||||
if( !line ) // end of file
|
||||
break;
|
||||
std::string err;
|
||||
json11::Json json_parser = json11::Json::parse( TO_UTF8( data ), err );
|
||||
|
||||
wxString text( line );
|
||||
text.Trim( true );
|
||||
text.Trim( false );
|
||||
if( !err.empty() )
|
||||
return false;
|
||||
|
||||
// Search for lines starting by '%', others are not usefull
|
||||
if( text.StartsWith( "%TJ.L.", &data ) // First job file syntax
|
||||
|| text.StartsWith( "%TJ.L_", &data ) // current job file syntax
|
||||
)
|
||||
for( auto& entry : json_parser["FilesAttributes"].array_items() )
|
||||
{
|
||||
parseTJLayerString( data );
|
||||
continue;
|
||||
//wxLogMessage( entry.dump().c_str() );
|
||||
std::string name = entry["Path"].string_value();
|
||||
//wxLogMessage( name.c_str() );
|
||||
m_GerberFiles.Add( FormatStringFromGerber( name ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jobfileReader.Rewind();
|
||||
|
||||
if( text.StartsWith( "M02" ) ) // End of file
|
||||
break;
|
||||
while( true )
|
||||
{
|
||||
line = jobfileReader.ReadLine();
|
||||
|
||||
if( !line ) // end of file
|
||||
break;
|
||||
|
||||
wxString text( line );
|
||||
text.Trim( true );
|
||||
text.Trim( false );
|
||||
|
||||
// Search for lines starting by '%', others are not usefull
|
||||
if( text.StartsWith( "%TJ.L.", &data ) // First job file syntax
|
||||
|| text.StartsWith( "%TJ.L_", &data ) // current job file syntax
|
||||
)
|
||||
{
|
||||
parseTJLayerString( data );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( text.StartsWith( "M02" ) ) // End of file
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,251 @@
|
|||
/* json11
|
||||
*
|
||||
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||
*
|
||||
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||
* object (std::map).
|
||||
*
|
||||
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||
* Json::parse (static) to parse a std::string as a Json object.
|
||||
*
|
||||
* Internally, the various types of Json object are represented by the JsonValue class
|
||||
* hierarchy.
|
||||
*
|
||||
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||
* provides integer helpers.
|
||||
*
|
||||
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||
* will be exact for +/- 275 years.)
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2013 Dropbox, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <initializer_list>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER <= 1800 // VS 2013
|
||||
#ifndef noexcept
|
||||
#define noexcept throw()
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace json11 {
|
||||
enum JsonParse
|
||||
{
|
||||
STANDARD, COMMENTS
|
||||
};
|
||||
|
||||
class JsonValue;
|
||||
|
||||
class Json final
|
||||
{
|
||||
public:
|
||||
// Types
|
||||
enum Type
|
||||
{
|
||||
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||
};
|
||||
|
||||
// Array and object typedefs
|
||||
typedef std::vector<Json> array;
|
||||
typedef std::map<std::string, Json> object;
|
||||
|
||||
// Constructors for the various types of JSON value.
|
||||
Json() noexcept; // NUL
|
||||
Json( std::nullptr_t ) noexcept; // NUL
|
||||
Json( double value ); // NUMBER
|
||||
Json( int value ); // NUMBER
|
||||
Json( bool value ); // BOOL
|
||||
Json( const std::string& value ); // STRING
|
||||
Json( std::string&& value ); // STRING
|
||||
Json( const char* value ); // STRING
|
||||
Json( const array& values ); // ARRAY
|
||||
Json( array&& values ); // ARRAY
|
||||
Json( const object& values ); // OBJECT
|
||||
Json( object&& values ); // OBJECT
|
||||
|
||||
// Implicit constructor: anything with a to_json() function.
|
||||
template <class T, class = decltype(& T::to_json)>
|
||||
Json( const T& t ) : Json( t.to_json() ) {}
|
||||
|
||||
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||
template <class M, typename std::enable_if<
|
||||
std::is_constructible<std::string,
|
||||
decltype(std::declval<M>().begin()->first)>::value
|
||||
&& std::is_constructible<Json,
|
||||
decltype(std::declval<M>().begin()->second)>::value,
|
||||
int>::type = 0>
|
||||
Json( const M& m ) : Json( object( m.begin(), m.end() ) ) {}
|
||||
|
||||
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||
template <class V, typename std::enable_if<
|
||||
std::is_constructible<Json, decltype( * std::declval<V>().begin() )>::value,
|
||||
int>::type = 0>
|
||||
Json( const V& v ) : Json( array( v.begin(), v.end() ) ) {}
|
||||
|
||||
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||
// Json(bool(some_pointer)) if that behavior is desired.
|
||||
Json( void* ) = delete;
|
||||
|
||||
// Accessors
|
||||
Type type() const;
|
||||
|
||||
bool is_null() const { return type() == NUL; }
|
||||
bool is_number() const { return type() == NUMBER; }
|
||||
bool is_bool() const { return type() == BOOL; }
|
||||
bool is_string() const { return type() == STRING; }
|
||||
bool is_array() const { return type() == ARRAY; }
|
||||
bool is_object() const { return type() == OBJECT; }
|
||||
|
||||
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||
// can both be applied to a NUMBER-typed object.
|
||||
double number_value() const;
|
||||
int int_value() const;
|
||||
|
||||
// Return the enclosed value if this is a boolean, false otherwise.
|
||||
bool bool_value() const;
|
||||
|
||||
// Return the enclosed string if this is a string, "" otherwise.
|
||||
const std::string& string_value() const;
|
||||
|
||||
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||
const array& array_items() const;
|
||||
|
||||
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||
const object& object_items() const;
|
||||
|
||||
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||
const Json& operator[]( size_t i ) const;
|
||||
|
||||
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||
const Json& operator[]( const std::string& key ) const;
|
||||
|
||||
// Serialize.
|
||||
void dump( std::string& out ) const;
|
||||
|
||||
std::string dump() const
|
||||
{
|
||||
std::string out;
|
||||
|
||||
dump( out );
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||
static Json parse( const std::string& in,
|
||||
std::string& err,
|
||||
JsonParse strategy = JsonParse::STANDARD );
|
||||
|
||||
static Json parse( const char* in,
|
||||
std::string& err,
|
||||
JsonParse strategy = JsonParse::STANDARD )
|
||||
{
|
||||
if( in )
|
||||
{
|
||||
return parse( std::string( in ), err, strategy );
|
||||
}
|
||||
else
|
||||
{
|
||||
err = "null input";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse multiple objects, concatenated or separated by whitespace
|
||||
static std::vector<Json> parse_multi( const std::string& in,
|
||||
std::string::size_type& parser_stop_pos,
|
||||
std::string& err,
|
||||
JsonParse strategy = JsonParse::STANDARD );
|
||||
|
||||
static inline std::vector<Json> parse_multi( const std::string& in,
|
||||
std::string& err,
|
||||
JsonParse strategy = JsonParse::STANDARD )
|
||||
{
|
||||
std::string::size_type parser_stop_pos;
|
||||
|
||||
return parse_multi( in, parser_stop_pos, err, strategy );
|
||||
}
|
||||
|
||||
bool operator==( const Json& rhs ) const;
|
||||
bool operator<( const Json& rhs ) const;
|
||||
|
||||
bool operator!=( const Json& rhs ) const { return !(*this == rhs); }
|
||||
bool operator<=( const Json& rhs ) const { return !(rhs < *this); }
|
||||
bool operator>( const Json& rhs ) const { return rhs < *this; }
|
||||
bool operator>=( const Json& rhs ) const { return !(*this < rhs); }
|
||||
|
||||
/* has_shape(types, err)
|
||||
*
|
||||
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||
* the given type. If not, return false and set err to a descriptive message.
|
||||
*/
|
||||
typedef std::initializer_list<std::pair<std::string, Type> > shape;
|
||||
bool has_shape( const shape& types, std::string& err ) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<JsonValue> m_ptr;
|
||||
};
|
||||
|
||||
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||
class JsonValue
|
||||
{
|
||||
protected:
|
||||
friend class Json;
|
||||
friend class JsonInt;
|
||||
friend class JsonDouble;
|
||||
virtual Json::Type type() const = 0;
|
||||
virtual bool equals( const JsonValue* other ) const = 0;
|
||||
virtual bool less( const JsonValue* other ) const = 0;
|
||||
virtual void dump( std::string& out ) const = 0;
|
||||
virtual double number_value() const;
|
||||
virtual int int_value() const;
|
||||
virtual bool bool_value() const;
|
||||
virtual const std::string& string_value() const;
|
||||
virtual const Json::array& array_items() const;
|
||||
virtual const Json& operator[]( size_t i ) const;
|
||||
virtual const Json::object& object_items() const;
|
||||
virtual const Json& operator[]( const std::string& key ) const;
|
||||
|
||||
virtual ~JsonValue() {}
|
||||
};
|
||||
} // namespace json11
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
|
||||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2018 Jean_Pierre Charras <jp.charras at wanadoo.fr>
|
||||
* Copyright (C) 1992-2018 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
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
/**
|
||||
* @file gendrill_gerber_writer.cpp
|
||||
* @brief Functions to create drill files in gerber X2 format.
|
||||
* @brief Functions to create the Gerber job file in JSON format.
|
||||
*/
|
||||
|
||||
#include <fctsys.h>
|
||||
|
@ -52,6 +52,8 @@ GERBER_JOBFILE_WRITER::GERBER_JOBFILE_WRITER( BOARD* aPcb, REPORTER* aReporter )
|
|||
m_pcb = aPcb;
|
||||
m_reporter = aReporter;
|
||||
m_conversionUnits = 1.0 / IU_PER_MM; // Gerber units = mm
|
||||
m_useJSONformat = true;
|
||||
m_indent = 0;
|
||||
}
|
||||
|
||||
enum ONSIDE GERBER_JOBFILE_WRITER::hasSilkLayers()
|
||||
|
@ -113,10 +115,38 @@ const char* GERBER_JOBFILE_WRITER::sideKeyValue( enum ONSIDE aValue )
|
|||
}
|
||||
|
||||
|
||||
bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
|
||||
{
|
||||
bool success;
|
||||
wxString msg;
|
||||
|
||||
if( m_useJSONformat )
|
||||
success = CreateJSONJobFile( aFullFilename );
|
||||
else
|
||||
success = CreateGbrJobFile( aFullFilename );
|
||||
|
||||
if( !success )
|
||||
{
|
||||
if( m_reporter )
|
||||
{
|
||||
msg.Printf( _( "Unable to create job file \"%s\"" ), aFullFilename );
|
||||
m_reporter->Report( msg, REPORTER::RPT_ERROR );
|
||||
}
|
||||
}
|
||||
else if( m_reporter )
|
||||
{
|
||||
msg.Printf( _( "Create Gerber job file \"%s\"" ), aFullFilename );
|
||||
m_reporter->Report( msg, REPORTER::RPT_ACTION );
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
extern void BuildGerberX2Header( const BOARD *aBoard, wxArrayString& aHeader );
|
||||
|
||||
|
||||
bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
|
||||
bool GERBER_JOBFILE_WRITER::CreateGbrJobFile( const wxString& aFullFilename )
|
||||
{
|
||||
// Note: in Gerber job file, dimensions are in mm, and are floating numbers
|
||||
FILE* jobFile = wxFopen( aFullFilename, "wt" );
|
||||
|
@ -124,14 +154,7 @@ bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
|
|||
wxString msg;
|
||||
|
||||
if( jobFile == nullptr )
|
||||
{
|
||||
if( m_reporter )
|
||||
{
|
||||
msg.Printf( _( "Unable to create job file \"%s\"" ), aFullFilename );
|
||||
m_reporter->Report( msg, REPORTER::RPT_ERROR );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LOCALE_IO dummy;
|
||||
|
||||
|
@ -378,11 +401,581 @@ bool GERBER_JOBFILE_WRITER::CreateJobFile( const wxString& aFullFilename )
|
|||
|
||||
fclose( jobFile );
|
||||
|
||||
if( m_reporter )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::addJSONHeader()
|
||||
{
|
||||
wxString text;
|
||||
openBlock();
|
||||
addJSONObject( "\"Header\":\n" );
|
||||
openBlock();
|
||||
|
||||
// Creates the GenerationSoftware
|
||||
addJSONObject( "\"GenerationSoftware\":\n" );
|
||||
openBlock();
|
||||
addJSONObject( "\"Vendor\": \"KiCad\",\n" );
|
||||
addJSONObject( "\"Application\": \"Pcbnew\",\n" );
|
||||
text.Printf( "\"Version\": \"%s\"\n", GetBuildVersion() );
|
||||
addJSONObject( text );
|
||||
closeBlockWithSep();
|
||||
|
||||
// creates the TF.CreationDate ext:
|
||||
// The attribute value must conform to the full version of the ISO 8601
|
||||
// date and time format, including time and time zone. Note that this is
|
||||
// the date the Gerber file was effectively created,
|
||||
// not the time the project of PCB was started
|
||||
wxDateTime date( wxDateTime::GetTimeNow() );
|
||||
// Date format: see http://www.cplusplus.com/reference/ctime/strftime
|
||||
wxString msg = date.Format( wxT( "%z" ) ); // Extract the time zone offset
|
||||
// The time zone offset format is + (or -) mm or hhmm (mm = number of minutes, hh = number of hours)
|
||||
// we want +(or -) hh:mm
|
||||
if( msg.Len() > 3 )
|
||||
msg.insert( 3, ":", 1 ),
|
||||
text.Printf( wxT( "\"CreationDate\": \"%s%s\"\n" ),date.FormatISOCombined(), msg );
|
||||
addJSONObject( text );
|
||||
|
||||
closeBlockWithSep();
|
||||
}
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::removeJSONSepararator()
|
||||
{
|
||||
if( m_JSONbuffer.Last() == ',' )
|
||||
{
|
||||
msg.Printf( _( "Create Gerber job file \"%s\"" ), aFullFilename );
|
||||
m_reporter->Report( msg, REPORTER::RPT_ACTION );
|
||||
m_JSONbuffer.RemoveLast();
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_JSONbuffer.Last() == '\n' )
|
||||
{
|
||||
m_JSONbuffer.RemoveLast();
|
||||
|
||||
if( m_JSONbuffer.Last() == ',' )
|
||||
m_JSONbuffer.RemoveLast();
|
||||
|
||||
m_JSONbuffer.Append( '\n' );
|
||||
}
|
||||
}
|
||||
|
||||
bool GERBER_JOBFILE_WRITER::CreateJSONJobFile( const wxString& aFullFilename )
|
||||
{
|
||||
// Note: in Gerber job file, dimensions are in mm, and are floating numbers
|
||||
FILE* jobFile = wxFopen( aFullFilename, "wt" );
|
||||
|
||||
m_JSONbuffer.Empty();
|
||||
m_indent = 0;
|
||||
|
||||
if( jobFile == nullptr )
|
||||
return false;
|
||||
|
||||
LOCALE_IO dummy;
|
||||
|
||||
// output the job file header
|
||||
addJSONHeader();
|
||||
|
||||
// Add the General Specs
|
||||
addJSONGeneralSpecs();
|
||||
|
||||
// Job file support a few design rules:
|
||||
addJSONDesignRules();
|
||||
|
||||
// output the gerber file list:
|
||||
addJSONFilesAttributes();
|
||||
|
||||
// output the board stackup:
|
||||
addJSONMaterialStackup();
|
||||
|
||||
// Close job file full block data
|
||||
removeJSONSepararator(); // remove the last separator
|
||||
closeBlock();
|
||||
|
||||
fputs( TO_UTF8( m_JSONbuffer ), jobFile );
|
||||
|
||||
fclose( jobFile );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::addJSONGeneralSpecs()
|
||||
{
|
||||
addJSONObject( "\"GeneralSpecs\":\n" );
|
||||
openBlock();
|
||||
|
||||
addJSONObject( "\"ProjectId\":\n" );
|
||||
openBlock();
|
||||
|
||||
// Creates the ProjectId. Format is (from Gerber file format doc):
|
||||
// ProjectId,<project id>,<project GUID>,<revision id>*%
|
||||
// <project id> is the name of the project, restricted to basic ASCII symbols only,
|
||||
// and comma not accepted
|
||||
// All illegal chars will be replaced by underscore
|
||||
// <project GUID> is a 32 hexadecimal digits string which is an unique id of a project.
|
||||
// This is a random 128-bit number expressed in 32 hexadecimal digits.
|
||||
// See en.wikipedia.org/wiki/GUID for more information
|
||||
// However Kicad does not handle such a project GUID, so it is built from the board name
|
||||
// Rem: <project id> accepts only ASCII 7 code (only basic ASCII codes are allowed in gerber files).
|
||||
wxFileName fn = m_pcb->GetFileName();
|
||||
wxString msg = fn.GetFullName();
|
||||
wxString guid;
|
||||
|
||||
// Build a 32 digits GUID from the board name:
|
||||
for( unsigned ii = 0; ii < msg.Len(); ii++ )
|
||||
{
|
||||
int cc1 = int( msg[ii] ) & 0x0F;
|
||||
int cc2 = ( int( msg[ii] ) >> 4) & 0x0F;
|
||||
guid << wxString::Format( wxT( "%X%X" ), cc2, cc1 );
|
||||
|
||||
if( guid.Len() >= 32 )
|
||||
break;
|
||||
}
|
||||
|
||||
// guid has 32 digits, so add missing digits
|
||||
int cnt = 32 - guid.Len();
|
||||
|
||||
if( cnt > 0 )
|
||||
guid.Append( '0', cnt );
|
||||
|
||||
// build the <project id> string: this is the board short filename (without ext)
|
||||
// and all non ASCII chars are replaced by '_'
|
||||
msg = fn.GetName();
|
||||
|
||||
// build the <rec> string. All non ASCII chars and comma are replaced by '_'
|
||||
wxString rev = m_pcb->GetTitleBlock().GetRevision();
|
||||
|
||||
if( rev.IsEmpty() )
|
||||
rev = wxT( "rev?" );
|
||||
|
||||
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", msg.ToAscii() ) );
|
||||
addJSONObject( wxString::Format( "\"GUID\": \"%s\",\n", guid ) );
|
||||
addJSONObject( wxString::Format( "\"Revision\": \"%s\"\n", rev.ToAscii() ) );
|
||||
|
||||
closeBlockWithSep();
|
||||
|
||||
// output the bord size in mm:
|
||||
EDA_RECT brect = m_pcb->GetBoardEdgesBoundingBox();
|
||||
addJSONObject( "\"Size\":\n" );
|
||||
openBlock();
|
||||
|
||||
addJSONObject( wxString::Format( "\"X\": %.3f,\n", brect.GetWidth()*m_conversionUnits ) );
|
||||
addJSONObject( wxString::Format( "\"Y\": %.3f\n", brect.GetHeight()*m_conversionUnits ) );
|
||||
closeBlockWithSep();
|
||||
|
||||
// Add some data to the JSON header, GeneralSpecs:
|
||||
// number of copper layers
|
||||
addJSONObject( wxString::Format( "\"LayerNumber\": %d,\n", m_pcb->GetCopperLayerCount() ) );
|
||||
|
||||
// Board thickness
|
||||
addJSONObject( wxString::Format( "\"BoardThickness\": %.3f,\n",
|
||||
m_pcb->GetDesignSettings().GetBoardThickness()*m_conversionUnits ) );
|
||||
|
||||
#if 0 // Not yet in use
|
||||
/* The board type according to IPC-2221. There are six primary board types:
|
||||
- Type 1 - Single-sided
|
||||
- Type 2 - Double-sided
|
||||
- Type 3 – Multilayer, TH components only
|
||||
- Type 4 – Multilayer, with TH, blind and/or buried vias.
|
||||
- Type 5 - Multilayer metal-core board, TH components only
|
||||
- Type 6 - Multilayer metal-core
|
||||
*/
|
||||
addJSONObject( wxString::Format( "\"IPC-2221-Type\": \"%d\",\n", 4 ) );
|
||||
|
||||
/* Via protection: key words:
|
||||
Ia Tented - Single-sided
|
||||
Ib Tented - Double-sided
|
||||
IIa Tented and Covered – Single-sided
|
||||
IIb Tented and Covered – Double-sided
|
||||
IIIa Plugged – Single-sided
|
||||
IIIb…….Plugged – Double-sided
|
||||
IVa…….Plugged and Covered – Single-sided
|
||||
IVb…….Plugged and Covered – Double-sided
|
||||
V Filled (fully plugged)
|
||||
VI Filled and Covered
|
||||
VIII Filled and Capped
|
||||
None…...No protection
|
||||
*/
|
||||
addJSONObject( wxString::Format( "\"ViaProtection\": \"%s\",\n", "Ib" ) );
|
||||
#endif
|
||||
removeJSONSepararator();
|
||||
closeBlockWithSep();
|
||||
}
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::addJSONFilesAttributes()
|
||||
{
|
||||
// Add the Files Attributes section in JSON format to m_JSONbuffer
|
||||
addJSONObject( "\"FilesAttributes\":\n" );
|
||||
openArrayBlock();
|
||||
|
||||
for( unsigned ii = 0; ii < m_params.m_GerberFileList.GetCount(); ii ++ )
|
||||
{
|
||||
wxString& name = m_params.m_GerberFileList[ii];
|
||||
PCB_LAYER_ID layer = m_params.m_LayerId[ii];
|
||||
wxString gbr_layer_id;
|
||||
bool skip_file = false; // true to skip files which should not be in job file
|
||||
const char* polarity = "Positive";
|
||||
|
||||
if( layer <= B_Cu )
|
||||
{
|
||||
gbr_layer_id = "Copper,L";
|
||||
|
||||
if( layer == B_Cu )
|
||||
gbr_layer_id << m_pcb->GetCopperLayerCount();
|
||||
else
|
||||
gbr_layer_id << layer+1;
|
||||
|
||||
gbr_layer_id << ",";
|
||||
|
||||
if( layer == B_Cu )
|
||||
gbr_layer_id << "Bot";
|
||||
else if( layer == F_Cu )
|
||||
gbr_layer_id << "Top";
|
||||
else
|
||||
gbr_layer_id << "Inr";
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
switch( layer )
|
||||
{
|
||||
case B_Adhes:
|
||||
gbr_layer_id = "Glue,Bot"; break;
|
||||
case F_Adhes:
|
||||
gbr_layer_id = "Glue,Top"; break;
|
||||
|
||||
case B_Paste:
|
||||
gbr_layer_id = "SolderPaste,Bot"; break;
|
||||
case F_Paste:
|
||||
gbr_layer_id = "SolderPaste,Top"; break;
|
||||
|
||||
case B_SilkS:
|
||||
gbr_layer_id = "Legend,Bot"; break;
|
||||
case F_SilkS:
|
||||
gbr_layer_id = "Legend,Top"; break;
|
||||
|
||||
case B_Mask:
|
||||
gbr_layer_id = "SolderMask,Bot"; polarity = "Negative"; break;
|
||||
case F_Mask:
|
||||
gbr_layer_id = "SolderMask,Top"; polarity = "Negative"; break;
|
||||
|
||||
case Edge_Cuts:
|
||||
gbr_layer_id = "Profile"; break;
|
||||
|
||||
case B_Fab:
|
||||
gbr_layer_id = "AssemblyDrawing,Bot"; break;
|
||||
case F_Fab:
|
||||
gbr_layer_id = "AssemblyDrawing,Top"; break;
|
||||
|
||||
case Dwgs_User:
|
||||
case Cmts_User:
|
||||
case Eco1_User:
|
||||
case Eco2_User:
|
||||
case Margin:
|
||||
case B_CrtYd:
|
||||
case F_CrtYd:
|
||||
skip_file = true; break;
|
||||
|
||||
default:
|
||||
skip_file = true;
|
||||
m_reporter->Report( "Unexpected layer id in job file",
|
||||
REPORTER::RPT_ERROR );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !skip_file )
|
||||
{
|
||||
// name can contain non ASCII7 chars.
|
||||
// Only ASCII7 chars are accepted in gerber files. others must be converted to
|
||||
// a gerber hexa sequence.
|
||||
std::string strname = formatStringToGerber( name );
|
||||
|
||||
openBlock();
|
||||
addJSONObject( wxString::Format( "\"Path\": \"%s\",\n", strname.c_str() ) );
|
||||
addJSONObject( wxString::Format( "\"FileFunction\": \"%s\",\n", gbr_layer_id ) ),
|
||||
addJSONObject( wxString::Format( "\"FilePolarity\": \"%s\"\n", polarity ) );
|
||||
closeBlockWithSep();
|
||||
}
|
||||
}
|
||||
// Close the file list:
|
||||
removeJSONSepararator(); // remove the last separator
|
||||
closeArrayBlockWithSep();
|
||||
}
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::addJSONDesignRules()
|
||||
{
|
||||
// Add the Design Rules section in JSON format to m_JSONbuffer
|
||||
// Job file support a few design rules:
|
||||
const BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
|
||||
NETCLASS defaultNC = *dsnSettings.GetDefault();
|
||||
int minclearanceOuter = defaultNC.GetClearance();
|
||||
bool hasInnerLayers = m_pcb->GetCopperLayerCount() > 2;
|
||||
|
||||
// Search a smaller clearance in other net classes, if any.
|
||||
for( NETCLASSES::const_iterator it = dsnSettings.m_NetClasses.begin();
|
||||
it != dsnSettings.m_NetClasses.end();
|
||||
++it )
|
||||
{
|
||||
NETCLASS netclass = *it->second;
|
||||
minclearanceOuter = std::min( minclearanceOuter, netclass.GetClearance() );
|
||||
}
|
||||
|
||||
// job file knows different clearance types.
|
||||
// Kicad knows only one clearance for pads and tracks
|
||||
int minclearance_track2track = minclearanceOuter;
|
||||
|
||||
// However, pads can have a specific clearance defined for a pad or a footprint,
|
||||
// and min clearance can be dependent on layers.
|
||||
// Search for a minimal pad clearance:
|
||||
int minPadClearanceOuter = defaultNC.GetClearance();
|
||||
int minPadClearanceInner = defaultNC.GetClearance();
|
||||
|
||||
for( MODULE* module : m_pcb->Modules() )
|
||||
{
|
||||
for( auto& pad : module->Pads() )
|
||||
{
|
||||
if( ( pad->GetLayerSet() & LSET::InternalCuMask() ).any() )
|
||||
minPadClearanceInner = std::min( minPadClearanceInner, pad->GetClearance() );
|
||||
|
||||
if( ( pad->GetLayerSet() & LSET::ExternalCuMask() ).any() )
|
||||
minPadClearanceOuter = std::min( minPadClearanceOuter, pad->GetClearance() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addJSONObject( "\"DesignRules\":\n" );
|
||||
openArrayBlock();
|
||||
|
||||
openBlock();
|
||||
addJSONObject( "\"Layers\": \"Outer\",\n" );
|
||||
|
||||
addJSONObject( "\"Values\":\n" );
|
||||
|
||||
openBlock();
|
||||
addJSONObject( wxString::Format( "\"PadToPad\": %.3f,\n", minPadClearanceOuter*m_conversionUnits ) );
|
||||
addJSONObject( wxString::Format( "\"PadToTrack\": %.3f,\n", minPadClearanceOuter*m_conversionUnits ) );
|
||||
addJSONObject( wxString::Format( "\"TrackToTrack\": %.3f,\n", minclearance_track2track*m_conversionUnits ) );
|
||||
|
||||
// Until this is changed in Kicad, use the same value for internal tracks
|
||||
int minclearanceInner = minclearanceOuter;
|
||||
|
||||
// Output the minimal track width
|
||||
int mintrackWidthOuter = INT_MAX;
|
||||
int mintrackWidthInner = INT_MAX;
|
||||
|
||||
for( TRACK* track : m_pcb->Tracks() )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
continue;
|
||||
|
||||
if( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
|
||||
mintrackWidthOuter = std::min( mintrackWidthOuter, track->GetWidth() );
|
||||
else
|
||||
mintrackWidthInner = std::min( mintrackWidthInner, track->GetWidth() );
|
||||
}
|
||||
|
||||
if( mintrackWidthOuter != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"MinLineWidth\": %.3f,\n",
|
||||
mintrackWidthOuter*m_conversionUnits ) );
|
||||
|
||||
// Output the minimal zone to xx clearance
|
||||
// Note: zones can have a zone clearance set to 0
|
||||
// if happens, the actual zone clearance is the clearance of its class
|
||||
minclearanceOuter = INT_MAX;
|
||||
minclearanceInner = INT_MAX;
|
||||
|
||||
for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
|
||||
{
|
||||
ZONE_CONTAINER* zone = m_pcb->GetArea( ii );
|
||||
|
||||
if( zone->GetIsKeepout() || !zone->IsOnCopperLayer() )
|
||||
continue;
|
||||
|
||||
int zclerance = zone->GetClearance();
|
||||
|
||||
if( zone->GetLayer() == B_Cu || zone->GetLayer() == F_Cu )
|
||||
minclearanceOuter = std::min( minclearanceOuter, zclerance );
|
||||
else
|
||||
minclearanceInner = std::min( minclearanceInner, zclerance );
|
||||
}
|
||||
|
||||
if( minclearanceOuter != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"TrackToRegion\": %.3f,\n",
|
||||
minclearanceOuter*m_conversionUnits ) );
|
||||
|
||||
if( minclearanceOuter != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"RegionToRegion\": %.3f,\n",
|
||||
minclearanceOuter*m_conversionUnits ) );
|
||||
|
||||
removeJSONSepararator(); // remove the last separator
|
||||
closeBlock();
|
||||
|
||||
|
||||
if( hasInnerLayers )
|
||||
{
|
||||
closeBlockWithSep();
|
||||
|
||||
openBlock();
|
||||
addJSONObject( "\"Layers\": \"Inner\",\n" );
|
||||
|
||||
addJSONObject( "\"Values\":\n" );
|
||||
openBlock();
|
||||
|
||||
addJSONObject( wxString::Format( "\"PadToPad\": %.3f,\n", minPadClearanceInner*m_conversionUnits ) );
|
||||
addJSONObject( wxString::Format( "\"PadToTrack\": %.3f,\n", minPadClearanceInner*m_conversionUnits ) );
|
||||
addJSONObject( wxString::Format( "\"TrackToTrack\": %.3f,\n", minclearance_track2track*m_conversionUnits ) );
|
||||
|
||||
if( mintrackWidthInner != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"MinLineWidth\": %.3f,\n", mintrackWidthInner*m_conversionUnits ) );
|
||||
|
||||
if( minclearanceInner != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"TrackToRegion\": %.3f,\n", minclearanceInner*m_conversionUnits ) );
|
||||
|
||||
if( minclearanceInner != INT_MAX )
|
||||
addJSONObject( wxString::Format( "\"RegionToRegion\": %.3f,\n", minclearanceInner*m_conversionUnits ) );
|
||||
|
||||
removeJSONSepararator(); // remove the last separator
|
||||
closeBlock();
|
||||
}
|
||||
|
||||
// Close DesignRules
|
||||
closeBlock();
|
||||
closeArrayBlockWithSep();
|
||||
}
|
||||
|
||||
|
||||
void GERBER_JOBFILE_WRITER::addJSONMaterialStackup()
|
||||
{
|
||||
// Add the Material Stackup section in JSON format to m_JSONbuffer
|
||||
addJSONObject( "\"MaterialStackup\":\n" );
|
||||
openArrayBlock();
|
||||
|
||||
// Build the candidates: only layers on a board are candidates:
|
||||
LSET maskLayer;
|
||||
|
||||
for( unsigned ii = 0; ii < m_params.m_GerberFileList.GetCount(); ii ++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = m_params.m_LayerId[ii];
|
||||
|
||||
if( layer <= B_Cu )
|
||||
maskLayer.set( layer );
|
||||
else
|
||||
{
|
||||
switch( layer )
|
||||
{
|
||||
case B_Paste:
|
||||
case F_Paste:
|
||||
case B_SilkS:
|
||||
case F_SilkS:
|
||||
case B_Mask:
|
||||
case F_Mask:
|
||||
maskLayer.set( layer );
|
||||
break;
|
||||
|
||||
case Edge_Cuts:
|
||||
case B_Adhes:
|
||||
case F_Adhes:
|
||||
case B_Fab:
|
||||
case F_Fab:
|
||||
case Dwgs_User:
|
||||
case Cmts_User:
|
||||
case Eco1_User:
|
||||
case Eco2_User:
|
||||
case Margin:
|
||||
case B_CrtYd:
|
||||
case F_CrtYd:
|
||||
break;
|
||||
|
||||
default:
|
||||
m_reporter->Report(
|
||||
wxString::Format( "Unexpected layer id %d in job file", layer ),
|
||||
REPORTER::RPT_ERROR );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build a candidate list (in reverse order: bottom to top):
|
||||
LSEQ list = maskLayer.SeqStackupBottom2Top();
|
||||
// Generate the list (top to bottom):
|
||||
for( int ii = list.size()-1; ii >= 0; --ii )
|
||||
{
|
||||
PCB_LAYER_ID layer = list[ii];
|
||||
wxString layer_type;
|
||||
wxString color;
|
||||
wxString dielectric;
|
||||
double thickness = 0.0; // layer thickness in mm
|
||||
|
||||
if( layer <= B_Cu )
|
||||
{
|
||||
layer_type = "Copper";
|
||||
//thickness = 0.035;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( layer )
|
||||
{
|
||||
case B_Paste:
|
||||
case F_Paste:
|
||||
layer_type = "SolderPaste";
|
||||
break;
|
||||
|
||||
case B_SilkS:
|
||||
case F_SilkS:
|
||||
//color = "White";
|
||||
layer_type = "Legend";
|
||||
break;
|
||||
|
||||
case B_Mask:
|
||||
case F_Mask:
|
||||
//color = "Green";
|
||||
//thickness = 0.025;
|
||||
layer_type = "SolderMask";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
openBlock();
|
||||
addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) );
|
||||
|
||||
if( !color.IsEmpty() )
|
||||
addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", color ) );
|
||||
|
||||
if( thickness > 0.0 )
|
||||
addJSONObject( wxString::Format( "\"Thickness\": %f,\n", thickness ) );
|
||||
|
||||
std::string strname = formatStringToGerber( m_pcb->GetLayerName( layer ) );
|
||||
addJSONObject( wxString::Format( "\"S_Notes\": \"Layer %s\",\n", strname.c_str() ) );
|
||||
removeJSONSepararator();
|
||||
closeBlockWithSep();
|
||||
|
||||
if( layer < B_Cu ) // Add dielectric between copper layers
|
||||
{
|
||||
dielectric = "FR4"; // Temporary
|
||||
|
||||
openBlock();
|
||||
addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", "Dielectric" ) );
|
||||
|
||||
if( thickness > 0.0 )
|
||||
addJSONObject( wxString::Format( "\"Thickness\": %f,\n", color ) );
|
||||
|
||||
if( !dielectric.IsEmpty() )
|
||||
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", dielectric ) );
|
||||
|
||||
addJSONObject( wxString::Format( "\"S_Notes\": \"Layers L%d/L%d\",\n",
|
||||
layer+1, layer+2 ) );
|
||||
removeJSONSepararator();
|
||||
closeBlockWithSep();
|
||||
}
|
||||
}
|
||||
|
||||
removeJSONSepararator();
|
||||
closeArrayBlockWithSep();
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/**
|
||||
* @file gerber_jobfile_writer.h
|
||||
* @brief Classes used in drill files, map files and report files generation.
|
||||
* @brief Classes used to generate a Gerber job file in JSON
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
|
||||
* Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors.
|
||||
* Copyright (C) 1992-2018 Jean_Pierre Charras <jp.charras at wanadoo.fr>
|
||||
* Copyright (C) 1992-2018 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
|
||||
|
@ -87,12 +87,26 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates an Excellon drill file
|
||||
* Creates a Gerber job file
|
||||
* @param aFullFilename = the full filename
|
||||
* @return true, or false if the file cannot be created
|
||||
*/
|
||||
bool CreateJobFile( const wxString& aFullFilename );
|
||||
|
||||
/**
|
||||
* Creates a Gerber job file in old gbr format
|
||||
* @param aFullFilename = the full filename
|
||||
* @return true, or false if the file cannot be created
|
||||
*/
|
||||
bool CreateGbrJobFile( const wxString& aFullFilename );
|
||||
|
||||
/**
|
||||
* Creates an Gerber job file in JSON format
|
||||
* @param aFullFilename = the full filename
|
||||
* @param aParams = true for a NPTH file, false for a PTH file
|
||||
* @return true, or false if the file cannot be created
|
||||
*/
|
||||
bool CreateJobFile( const wxString& aFullFilename );
|
||||
bool CreateJSONJobFile( const wxString& aFullFilename );
|
||||
|
||||
private:
|
||||
/** @return SIDE_NONE if no silk screen layer is in list
|
||||
|
@ -114,12 +128,94 @@ private:
|
|||
*/
|
||||
const char* sideKeyValue( enum ONSIDE aValue );
|
||||
|
||||
/**
|
||||
* Add the job file header in JSON format to m_JSONbuffer
|
||||
*/
|
||||
void addJSONHeader();
|
||||
|
||||
/**
|
||||
* Add the General Specs in JSON format to m_JSONbuffer
|
||||
*/
|
||||
void addJSONGeneralSpecs();
|
||||
|
||||
/**
|
||||
* Add the Files Attributes section in JSON format to m_JSONbuffer
|
||||
*/
|
||||
void addJSONFilesAttributes();
|
||||
|
||||
/**
|
||||
* Add the Material Stackup section in JSON format to m_JSONbuffer
|
||||
* This is the ordered list of stackup layers (mask, paste, silk, copper, dielectric)
|
||||
* used to make the physical board. Therefore not all layers are listed here
|
||||
*/
|
||||
void addJSONMaterialStackup();
|
||||
|
||||
/**
|
||||
* Add the Design Rules section in JSON format to m_JSONbuffer
|
||||
*/
|
||||
void addJSONDesignRules();
|
||||
|
||||
/**
|
||||
* Remove the comma if it is the last char in m_JSONbuffer,
|
||||
* or the previous char if the last char is a \n
|
||||
*/
|
||||
void removeJSONSepararator();
|
||||
|
||||
/**
|
||||
* add m_indent spaces in m_JSONbuffer
|
||||
*/
|
||||
void addIndent() { m_JSONbuffer.Append( ' ', m_indent ); }
|
||||
|
||||
/**
|
||||
* open a JSON block: add '{' and increment indentation
|
||||
*/
|
||||
void openBlock() { addIndent(); m_JSONbuffer << "{\n"; m_indent += 2; }
|
||||
|
||||
/**
|
||||
* open a JSON array block: add '[' and increment indentation
|
||||
*/
|
||||
void openArrayBlock() { addIndent(); m_JSONbuffer << "[\n"; m_indent += 2; }
|
||||
|
||||
/**
|
||||
* close a JSON block: decrement indentation and add '}'
|
||||
*/
|
||||
void closeBlock() { m_indent -= 2; addIndent(); m_JSONbuffer << "}\n"; }
|
||||
|
||||
/**
|
||||
* close a JSON block: decrement indentation and add '}' and ','
|
||||
*/
|
||||
void closeBlockWithSep() { m_indent -= 2; addIndent(); m_JSONbuffer << "},\n"; }
|
||||
|
||||
/**
|
||||
* close a JSON array block: decrement indentation and add ']'
|
||||
*/
|
||||
void closeArrayBlock() { m_indent -= 2; addIndent(); m_JSONbuffer << "]\n"; }
|
||||
|
||||
/**
|
||||
* close a JSON array block: decrement indentation and add ']' and ','
|
||||
*/
|
||||
void closeArrayBlockWithSep() { m_indent -= 2; addIndent(); m_JSONbuffer << "],\n"; }
|
||||
|
||||
/**
|
||||
* Add aParam to m_JSONbuffer, with suitable indentation
|
||||
*/
|
||||
void addJSONObject( const wxString& aParam )
|
||||
{
|
||||
addIndent(); m_JSONbuffer << aParam;
|
||||
}
|
||||
void addJSONObject( const char* aParam )
|
||||
{
|
||||
addIndent(); m_JSONbuffer << aParam;
|
||||
}
|
||||
|
||||
private:
|
||||
BOARD* m_pcb; // The board
|
||||
REPORTER* m_reporter; // a reporter for messages (can be null)
|
||||
JOBFILE_PARAMS m_params; // the list of various prms and data to write in a job file
|
||||
double m_conversionUnits; // scaling factor to convert brd units to gerber units (mm)
|
||||
bool m_useJSONformat; // temporary option
|
||||
wxString m_JSONbuffer; // a buffer to build the JSON data
|
||||
int m_indent; // helper for JSON format: the current indentation value
|
||||
};
|
||||
|
||||
#endif // #ifndef GERBER_JOBFILE_WRITER_H
|
||||
|
|
Loading…
Reference in New Issue