OdbDesignLib
OdbDesign ODB++ Parsing Library
 
Loading...
Searching...
No Matches
ToolsFile.cpp
1//
2// Created by Jandody on 08/26/2025.
3//
4
5#include "ToolsFile.h"
6#include <fstream>
7#include "../parse_error.h"
8#include "../invalid_odb_error.h"
9#include "Logger.h"
10#include "str_utils.h"
11#include "../../Constants.h"
12#include <ArchiveExtractor.h>
13#include "../../Text/Utf8Sanitizer.h"
14
15namespace Odb::Lib::FileModel::Design
16{
17 ToolsFile::ToolsFile()
18 : m_directory("")
19 , m_path("")
20 , m_units("")
21 , m_thickness(0)
22 , m_user_params("")
23 {
24 }
25
26 ToolsFile::~ToolsFile()
27 {
28 m_toolsByNum.clear();
29 }
30
31 std::string ToolsFile::GetUnits() const
32 {
33 return m_units;
34 }
35
36 double ToolsFile::GetThickness() const
37 {
38 return m_thickness;
39 }
40
41 std::string ToolsFile::GetUserParams() const
42 {
43 return m_user_params;
44 }
45
46 const ToolsFile::ToolsMap& ToolsFile::GetTools() const
47 {
48 return m_toolsByNum;
49 }
50
51 bool Lib::FileModel::Design::ToolsFile::Parse(std::filesystem::path directory)
52 {
53 std::ifstream toolsFile;
54 int lineNumber = 0;
55 std::string line;
56
57 try
58 {
59 m_directory = directory;
60
61 loginfo("checking for extraction...");
62
63 std::filesystem::path toolsFilePath;
64 for (const std::string toolsFilename : TOOLS_FILENAMES)
65 {
66 loginfo("trying tools file: [" + toolsFilename + "]...");
67
68 toolsFilePath = Utils::ArchiveExtractor::getUncompressedFilePath(m_directory, toolsFilename);
69 if (exists(toolsFilePath) && is_regular_file(toolsFilePath))
70 {
71 loginfo("found tools file: [" + toolsFilePath.string() + "]");
72 break;
73 }
74 }
75
76 m_path = toolsFilePath;
77
78 loginfo("any extraction complete, parsing data...");
79
80 if (!std::filesystem::exists(m_path))
81 {
82 auto message = "tools file does not exist: [" + m_directory.string() + "]";
83 logwarn(message);
84 return true;
85 //throw invalid_odb_error(message.c_str());
86 }
87 else if (!std::filesystem::is_regular_file(m_path))
88 {
89 auto message = "tools is not a file: [" + m_path.string() + "]";
90 throw invalid_odb_error(message.c_str());
91 }
92
93 toolsFile.open(m_path.string(), std::ios::in);
94 if (!toolsFile.is_open())
95 {
96 auto message = "unable to open tools file: [" + m_path.string() + "]";
97 throw invalid_odb_error(message.c_str());
98 }
99
100 std::shared_ptr<ToolsRecord> pCurrentToolsRecord;
101 bool openBraceFound = false;
102
103 while (std::getline(toolsFile, line))
104 {
105 lineNumber++;
106
107 // trim whitespace from beginning and end of line
108 Utils::str_trim(line);
109 if (!line.empty())
110 {
111 std::stringstream lineStream(line);
112 //char firstChar = line[0];
113
114 if (line.find(Constants::COMMENT_TOKEN) == 0)
115 {
116 // comment line
117 }
118 else if (line.find(ToolsFile::UNITS_TOKEN) == 0)
119 {
120 // units line
121 std::string token;
122 if (!std::getline(lineStream, token, '='))
123 {
124 throw_parse_error(m_path, line, token, lineNumber);
125 }
126
127 m_units = token;
128 }
129 else if (line.find(ToolsFile::THICKNESS_TOKEN) == 0)
130 {
131 // thickness line
132 std::string value;
133 if (!std::getline(lineStream, value, '='))
134 {
135 throw_parse_error(m_path, line, value, lineNumber);
136 }
137
138 if (std::getline(lineStream, value))
139 {
140 Utils::str_trim(value);
141 m_thickness = std::stod(value);
142 }
143 else
144 {
145 // Obsolete, Default=0
146 m_thickness = 0;
147 }
148
149 }
150 else if (line.find(ToolsFile::USER_PARAMS_TOKEN) == 0)
151 {
152 // user_params line
153 std::string value;
154 if (!std::getline(lineStream, value, '='))
155 {
156 throw_parse_error(m_path, line, value, lineNumber);
157 }
158
159 if (std::getline(lineStream, value))
160 {
161 Utils::str_trim(value);
162 m_user_params = value;
163 }
164 }
165 else if (line.find(ToolsRecord::RECORD_TOKEN) == 0)
166 {
167 std::string token;
168 if (!(lineStream >> token))
169 {
170 throw_parse_error(m_path, line, token, lineNumber);
171 }
172
173 if (token != ToolsRecord::RECORD_TOKEN || pCurrentToolsRecord != nullptr)
174 {
175 throw_parse_error(m_path, line, token, lineNumber);
176 }
177
178 // open a new TOOL array record
179 pCurrentToolsRecord = std::make_shared<ToolsRecord>();
180
181 if (lineStream >> token)
182 {
183 // open brace is on same line as tool record open token
184 if (token == Constants::ARRAY_RECORD_OPEN_TOKEN)
185 {
186 openBraceFound = true;
187 }
188 }
189 }
190 else if (line.find(Constants::ARRAY_RECORD_OPEN_TOKEN) == 0)
191 {
192 // no current opening of a tool array record found yet
193 if (pCurrentToolsRecord == nullptr)
194 {
195 throw_parse_error(m_path, line, "", lineNumber);
196 }
197
198 // found another open brace while still parsing after the previous record's open brace
199 if (openBraceFound)
200 {
201 throw_parse_error(m_path, line, "", lineNumber);
202 }
203
204 openBraceFound = true;
205 }
206 else if (line.find(Constants::ARRAY_RECORD_CLOSE_TOKEN) == 0)
207 {
208 if (pCurrentToolsRecord != nullptr && openBraceFound && pCurrentToolsRecord->toolNum != 0)
209 {
210 m_toolsByNum[pCurrentToolsRecord->toolNum] = pCurrentToolsRecord;
211 pCurrentToolsRecord.reset();
212 openBraceFound = false;
213 }
214 else
215 {
216 // found a close brace but aren't currently parsing a tools map record
217 throw_parse_error(m_path, line, "", lineNumber);
218 }
219 }
220 else
221 {
222 std::string attribute;
223 std::string value;
224
225 if (!std::getline(lineStream, attribute, '='))
226 {
227 throw_parse_error(m_path, line, "", lineNumber);
228 }
229
230 if (std::getline(lineStream, value))
231 {
232 Utils::str_trim(attribute);
233 Utils::str_trim(value);
234
235 if (pCurrentToolsRecord != nullptr && openBraceFound)
236 {
237 if (attribute == ToolsRecord::NUM_KEY || attribute == "num")
238 {
239 pCurrentToolsRecord->toolNum = std::stoi(value);
240 }
241 else if (attribute == ToolsRecord::TYPE_KEY || attribute == "type")
242 {
243 if (ToolsRecord::typeMap.contains(value))
244 {
245 pCurrentToolsRecord->type = ToolsRecord::typeMap.getValue(value);
246 }
247 else
248 {
249 throw_parse_error(m_path, line, attribute, lineNumber);
250 }
251 }
252 else if (attribute == ToolsRecord::TYPE2_KEY || attribute == "type2")
253 {
254 if (ToolsRecord::type2Map.contains(value))
255 {
256 auto type = pCurrentToolsRecord->type;
257 auto type2 = ToolsRecord::type2Map.getValue(value);
258
259 if (type2 != ToolsRecord::Type2::Standard)
260 {
261 bool invalid = false;
262 switch (type2)
263 {
264 case ToolsRecord::Type2::PressFit:
265 invalid = (type != ToolsRecord::Type::Plated);
266 break;
267
268 case ToolsRecord::Type2::Photo:
269 case ToolsRecord::Type2::Laser:
270 invalid = (type != ToolsRecord::Type::Via);
271 break;
272 }
273 if (invalid)
274 {
275 throw_parse_error(m_path, line, attribute, lineNumber);
276 }
277 }
278 pCurrentToolsRecord->type2 = type2;
279 }
280 else
281 {
282 pCurrentToolsRecord->type2 = ToolsRecord::Type2::Standard;
283 }
284 }
285 else if (attribute == ToolsRecord::MIN_TOL_KEY || attribute == "min_tol")
286 {
287 pCurrentToolsRecord->minTOL = std::stod(value);
288 }
289 else if (attribute == ToolsRecord::MAX_TOL_KEY || attribute == "max_tol")
290 {
291 pCurrentToolsRecord->maxTOL = std::stod(value);
292 }
293 else if (attribute == ToolsRecord::BIT_KEY || attribute == "bit")
294 {
295 pCurrentToolsRecord->drillBit = value;
296 }
297 else if (attribute == ToolsRecord::FINISH_SIZE_KEY || attribute == "finish_size")
298 {
299 pCurrentToolsRecord->finishSize = std::stod(value);
300 }
301 else if (attribute == ToolsRecord::DRILL_SIZE_KEY || attribute == "drill_size")
302 {
303 pCurrentToolsRecord->drillSize = std::stod(value);
304 }
305 else
306 {
307 throw_parse_error(m_path, line, attribute, lineNumber);
308 }
309 }
310 else
311 {
312 // found a name-value pair but aren't currently parsing a step or layer array record
313 //return false;
314 throw_parse_error(m_path, line, attribute, lineNumber);
315 }
316
317 }
318
319 }
320 }
321 }
322
323 toolsFile.close();
324 }
325 catch (parse_error& pe)
326 {
327 auto m = pe.toString("Parse Error:");
328 logerror(m);
329 toolsFile.close();
330 throw pe;
331 }
332 catch (std::exception& e)
333 {
334 parse_info pi(m_path, line, lineNumber);
335 const auto m = pi.toString();
336 logexception_msg(e, m);
337 toolsFile.close();
338 throw e;
339 }
340
341 return true;
342 }
343
344 bool ToolsFile::Save(std::ostream& os)
345 {
346 os << Constants::UNITS_TOKEN << " = " << m_units << std::endl;
347 os << ToolsFile::THICKNESS_TOKEN << " = " << std::to_string(m_thickness) << std::endl;
348 os << ToolsFile::USER_PARAMS_TOKEN << " = " << m_user_params << std::endl;
349
350 for (const auto& [toolNum, tool_info] : m_toolsByNum)
351 {
352 os << ToolsRecord::RECORD_TOKEN << " " << Constants::ARRAY_RECORD_OPEN_TOKEN << std::endl;
353
354 os << '\t' << ToolsRecord::NUM_KEY << "=" << toolNum << std::endl;
355 os << '\t' << ToolsRecord::TYPE_KEY << "=" << ToolsRecord::typeMap.getValue(tool_info->type) << std::endl;
356 os << '\t' << ToolsRecord::TYPE2_KEY << "=" << ToolsRecord::type2Map.getValue(tool_info->type2) << std::endl;
357 os << '\t' << ToolsRecord::MIN_TOL_KEY << "=" << tool_info->minTOL << std::endl;
358 os << '\t' << ToolsRecord::MAX_TOL_KEY << "=" << tool_info->maxTOL << std::endl;
359 os << '\t' << ToolsRecord::BIT_KEY << "=" << tool_info->drillBit << std::endl;
360 os << '\t' << ToolsRecord::FINISH_SIZE_KEY << "=" << tool_info->finishSize << std::endl;
361 os << '\t' << ToolsRecord::DRILL_SIZE_KEY << "=" << tool_info->drillSize << std::endl;
362
363 os << Constants::ARRAY_RECORD_CLOSE_TOKEN << std::endl;
364 os << std::endl;
365 }
366
367 return true;
368 }
369
370 std::unique_ptr<Odb::Lib::Protobuf::ToolsFile::ToolsRecord>Odb::Lib::FileModel::Design::ToolsFile::ToolsRecord::to_protobuf() const
371 {
372 std::unique_ptr<Odb::Lib::Protobuf::ToolsFile::ToolsRecord> pToolsRecordMessage(new Odb::Lib::Protobuf::ToolsFile::ToolsRecord);
373 pToolsRecordMessage->set_tool_num(toolNum);
374 pToolsRecordMessage->set_type(static_cast<Odb::Lib::Protobuf::ToolsFile::ToolsRecord::Type>(type));
375 pToolsRecordMessage->set_type2(static_cast<Odb::Lib::Protobuf::ToolsFile::ToolsRecord::Type2>(type2));
376 pToolsRecordMessage->set_min_tol(minTOL);
377 pToolsRecordMessage->set_max_tol(maxTOL);
378 pToolsRecordMessage->set_drill_bit(Odb::Lib::Text::ToUtf8(drillBit));
379 pToolsRecordMessage->set_finish_size(finishSize);
380 pToolsRecordMessage->set_drill_size(drillSize);
381
382 return pToolsRecordMessage;
383 }
384
385 void Odb::Lib::FileModel::Design::ToolsFile::ToolsRecord::from_protobuf(const Odb::Lib::Protobuf::ToolsFile::ToolsRecord& message)
386 {
387 toolNum = message.tool_num();
388 type = static_cast<Type>(message.type());
389 type2 = static_cast<Type2>(message.type2());
390 minTOL = message.min_tol();
391 maxTOL = message.max_tol();
392 drillBit = message.drill_bit();
393 finishSize = message.finish_size();
394 drillSize = message.drill_size();
395 }
396
397 std::unique_ptr<Odb::Lib::Protobuf::ToolsFile> Lib::FileModel::Design::ToolsFile::to_protobuf() const
398 {
399 auto message = std::make_unique<Odb::Lib::Protobuf::ToolsFile>();
400
401 message->set_directory(Odb::Lib::Text::ToUtf8(m_directory.string()));
402 message->set_path(Odb::Lib::Text::ToUtf8(m_path.string()));
403 message->set_units(Odb::Lib::Text::ToUtf8(m_units));
404
405 message->set_thickness(m_thickness);
406 message->set_user_params(Odb::Lib::Text::ToUtf8(m_user_params));
407
408 auto* tools_map = message->mutable_tools();
409 for (const auto& [toolNum, toolsRecord] : m_toolsByNum)
410 {
411 auto& recordMsg = (*tools_map)[toolNum];
412 recordMsg.CopyFrom(*toolsRecord->to_protobuf());
413 }
414
415 return message;
416 }
417
418 void Lib::FileModel::Design::ToolsFile::from_protobuf(const Odb::Lib::Protobuf::ToolsFile& message)
419 {
420 m_directory = message.directory();
421 m_path = message.path();
422
423 m_units = message.units();
424 m_thickness = message.thickness();
425 m_user_params = message.user_params();
426
427 for (const auto& [toolNum, toolsRecord] : message.tools())
428 {
429 auto ptoolsRecord = std::make_shared<ToolsRecord>();
430 ptoolsRecord->from_protobuf(toolsRecord);
431 m_toolsByNum.emplace(toolNum, ptoolsRecord);
432 }
433 }
434
435} // namespace Odb::Lib::FileModel::Design