KiCad PCB EDA Suite
Loading...
Searching...
No Matches
odb_test_utils.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "odb_test_utils.h"
21
22#include <algorithm>
23#include <fstream>
24#include <map>
25#include <memory>
26#include <sstream>
27
28#include <board.h>
29#include <core/utf8.h>
30#include <footprint.h>
31#include <pad.h>
32#include <reporter.h>
34#include <richio.h>
35
36namespace fs = std::filesystem;
37
38
39fs::path ExportOdb( const BOARD& aBoard, const fs::path& aDir, const std::string& aUnits,
40 const std::string& aSigfig, REPORTER* aReporter )
41{
42 fs::create_directories( aDir );
43
44 PCB_IO_ODBPP odbExporter;
45
46 if( aReporter )
47 odbExporter.SetReporter( aReporter );
48
49 std::map<std::string, UTF8> props;
50 props["units"] = aUnits;
51 props["sigfig"] = aSigfig;
52
53 odbExporter.SaveBoard( wxString::FromUTF8( aDir.string() ), const_cast<BOARD&>( aBoard ), &props );
54
55 return aDir;
56}
57
58
59std::vector<std::string> ReadLines( const fs::path& aFile )
60{
61 std::vector<std::string> lines;
62 std::ifstream stream( aFile );
63 std::string line;
64
65 while( std::getline( stream, line ) )
66 lines.push_back( line );
67
68 return lines;
69}
70
71
73{
74 static std::vector<std::unique_ptr<KI_TEST::SCOPED_TEMP_DIR>> dirs;
75 dirs.emplace_back( std::make_unique<KI_TEST::SCOPED_TEMP_DIR>( wxS( "odb_attributes" ) ) );
76 return *dirs.back();
77}
78
79
80size_t CountPads( const BOARD& aBoard, PAD_PROP aProperty, PCB_LAYER_ID aLayer )
81{
82 size_t count = 0;
83
84 for( const FOOTPRINT* footprint : aBoard.Footprints() )
85 {
86 for( const PAD* pad : footprint->Pads() )
87 {
88 if( pad->GetProperty() == aProperty && pad->IsOnLayer( aLayer ) )
89 ++count;
90 }
91 }
92
93 return count;
94}
95
96
97OdbFeatureFile::OdbFeatureFile( const fs::path& aFile )
98{
99 for( const std::string& line : ReadLines( aFile ) )
100 {
101 if( line.rfind( "@", 0 ) == 0 )
102 {
103 size_t space = line.find( ' ' );
104
105 if( space != std::string::npos )
106 m_names.emplace( line.substr( space + 1 ), line.substr( 1, space - 1 ) );
107 }
108 else if( line.rfind( "CMP ", 0 ) == 0
109 || ( line.size() > 2 && line[1] == ' '
110 && ( line[0] == 'P' || line[0] == 'L' || line[0] == 'A' || line[0] == 'S' ) ) )
111 {
112 m_records.push_back( line );
113 }
114 }
115}
116
117
118size_t OdbFeatureFile::CountRecordsWithOption( const std::string& aName, const std::string& aOption ) const
119{
120 static const std::map<std::string, std::vector<std::string>> options = {
121 { ".pad_usage", { "toeprint", "via", "g_fiducial", "l_fiducial", "tooling_hole", "bond_finger" } },
122 { ".drill", { "plated", "non_plated", "via" } },
123 { ".plated_type", { "standard", "press_fit" } },
124 { ".via_type", { "drilled", "laser", "photo" } },
125 { ".comp_mount_type", { "other", "smd", "tht", "pressfit" } }
126 };
127
128 auto found = options.find( aName );
129
130 if( found == options.end() )
131 return 0;
132
133 const auto& values = found->second;
134 auto option = std::find( values.begin(), values.end(), aOption );
135
136 if( option == values.end() )
137 return 0;
138
139 return CountRecordsWithValue( aName, std::to_string( std::distance( values.begin(), option ) ) );
140}
141
142
143size_t OdbFeatureFile::CountRecordsWithFlag( const std::string& aName ) const
144{
145 return CountRecordsWithValue( aName, std::string() );
146}
147
148
149size_t OdbFeatureFile::CountRecordsWithInteger( const std::string& aName, int aValue ) const
150{
151 return CountRecordsWithValue( aName, std::to_string( aValue ) );
152}
153
154
155size_t OdbFeatureFile::CountRecordsWithValue( const std::string& aName, const std::string& aValue ) const
156{
157 auto name = m_names.find( aName );
158
159 if( name == m_names.end() )
160 return 0;
161
162 size_t count = 0;
163
164 for( const std::string& record : m_records )
165 {
166 size_t start = record.find( ';' );
167
168 if( start == std::string::npos )
169 continue;
170
171 std::stringstream attributes( record.substr( start + 1 ) );
172 std::string attribute;
173
174 while( std::getline( attributes, attribute, ',' ) )
175 {
176 if( attribute == name->second + ( aValue.empty() ? "" : "=" + aValue ) )
177 {
178 ++count;
179 break;
180 }
181 }
182 }
183
184 return count;
185}
186
187
188ODB_PRODUCT::ODB_PRODUCT( const fs::path& aRoot )
189{
190 wxString stepName;
191 wxString block;
192 ODB_MATRIX_ROW row;
193
194 FILE_LINE_READER reader( wxString::FromUTF8( ( aRoot / "matrix" / "matrix" ).string() ) );
195
196 while( const char* line = reader.ReadLine() )
197 {
198 wxString text = wxString::FromUTF8( line ).Strip( wxString::both );
199
200 if( text.EndsWith( wxS( "{" ) ) )
201 {
202 block = text.BeforeLast( '{' ).Strip( wxString::both );
203 row = ODB_MATRIX_ROW();
204 continue;
205 }
206
207 if( text == wxS( "}" ) )
208 {
209 if( block == wxS( "LAYER" ) )
210 m_matrix.push_back( row );
211
212 block.clear();
213 continue;
214 }
215
216 wxString key = text.BeforeFirst( '=' ).Strip( wxString::both );
217 wxString value = text.AfterFirst( '=' ).Strip( wxString::both );
218
219 if( block == wxS( "STEP" ) && key == wxS( "NAME" ) && stepName.IsEmpty() )
220 {
221 stepName = value;
222 }
223 else if( block == wxS( "LAYER" ) )
224 {
225 if( key == wxS( "ROW" ) )
226 row.m_row = wxAtoi( value );
227 else if( key == wxS( "CONTEXT" ) )
228 row.m_context = value;
229 else if( key == wxS( "TYPE" ) )
230 row.m_type = value;
231 else if( key == wxS( "NAME" ) )
232 row.m_name = value;
233 else if( key == wxS( "POLARITY" ) )
234 row.m_polarity = value;
235 else if( key == wxS( "START_NAME" ) )
236 row.m_startName = value;
237 else if( key == wxS( "END_NAME" ) )
238 row.m_endName = value;
239 else if( key == wxS( "ADD_TYPE" ) )
240 row.m_addType = value;
241 else if( key == wxS( "ID" ) )
242 row.m_id = value;
243 else if( key == wxS( "REF" ) )
244 row.m_ref = value;
245 else if( key == wxS( "CU_TOP" ) )
246 row.m_cuTop = value;
247 else if( key == wxS( "CU_BOTTOM" ) )
248 row.m_cuBottom = value;
249 else if( key == wxS( "DIELECTRIC_TYPE" ) )
250 row.m_dielectricType = value;
251 }
252 }
253
254 std::error_code ec;
255
256 for( const fs::directory_entry& entry : fs::directory_iterator( aRoot / "steps", ec ) )
257 {
258 if( entry.is_directory() && wxString::FromUTF8( entry.path().filename().string() ).IsSameAs( stepName, false ) )
259 m_stepDir = entry.path();
260 }
261}
262
263
264wxString ODB_PRODUCT::LayerFile( const wxString& aLayer, const wxString& aFile ) const
265{
266 fs::path layersRoot = m_stepDir / "layers";
267 fs::path layerDir = layersRoot / aLayer.Lower().ToStdString();
268 std::error_code ec;
269
270 for( const fs::directory_entry& entry : fs::directory_iterator( layersRoot, ec ) )
271 {
272 if( entry.is_directory() && wxString::FromUTF8( entry.path().filename().string() ).IsSameAs( aLayer, false ) )
273 layerDir = entry.path();
274 }
275
276 return wxString::FromUTF8( ( layerDir / aFile.ToStdString() ).string() );
277}
const char * name
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const FOOTPRINTS & Footprints() const
Definition board.h:464
A LINE_READER that reads from an open file.
Definition richio.h:157
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:202
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Definition io_base.h:89
std::vector< ODB_MATRIX_ROW > m_matrix
std::filesystem::path m_stepDir
wxString LayerFile(const wxString &aLayer, const wxString &aFile) const
Path of a file in a layer directory, matched to the directory name without regard to case.
ODB_PRODUCT(const std::filesystem::path &aRoot)
std::map< std::string, std::string > m_names
std::vector< std::string > m_records
size_t CountRecordsWithInteger(const std::string &aName, int aValue) const
size_t CountRecordsWithOption(const std::string &aName, const std::string &aOption) const
OdbFeatureFile(const std::filesystem::path &aFile)
size_t CountRecordsWithValue(const std::string &aName, const std::string &aValue) const
size_t CountRecordsWithFlag(const std::string &aName) const
Definition pad.h:61
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
size_t CountPads(const BOARD &aBoard, PAD_PROP aProperty, PCB_LAYER_ID aLayer)
fs::path ExportOdb(const BOARD &aBoard, const fs::path &aDir, const std::string &aUnits, const std::string &aSigfig, REPORTER *aReporter)
std::vector< std::string > ReadLines(const fs::path &aFile)
const KI_TEST::SCOPED_TEMP_DIR & TempDir()
std::vector< std::string > ReadLines(const std::filesystem::path &aFile)
Read a text file into one string per line, with no trailing newline characters.
PAD_PROP
The set of pad properties used in Gerber files (Draw files, and P&P files) to define some properties ...
Definition padstack.h:113
One LAYER block of matrix/matrix.
wxString m_startName
wxString m_cuBottom
wxString m_dielectricType
wxString m_polarity