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