Refactor console handling and add support for processing all .datalog files

This commit is contained in:
yebreo1
2025-05-22 17:49:56 +08:00
parent 996aeb26ec
commit eb35cf340e
3 changed files with 361 additions and 275 deletions
+361 -275
View File
@@ -10,12 +10,35 @@
#include <iomanip> #include <iomanip>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <filesystem>
#include <future>
#include <windows.h>
#include <mutex>
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
std::mutex cout_mutex;
const int BUFFER_SIZE = 2048; const int BUFFER_SIZE = 2048;
// Helper function for Windows console cursor positioning
inline void set_cursor_position(int row, int col) {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = col - 1; // 0-based
pos.Y = row - 1; // 0-based
SetConsoleCursorPosition(hConsole, pos);
}
int get_console_width() {
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns = 80; // default
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
return columns;
}
inline string join(vector<string> const &strings, string delim) inline string join(vector<string> const &strings, string delim)
{ {
@@ -47,6 +70,8 @@ inline vector<string> tokenize(string s, string delimiter = " ")
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
system("cls");
ios::sync_with_stdio(false); ios::sync_with_stdio(false);
cin.tie(NULL); cin.tie(NULL);
@@ -58,11 +83,12 @@ int main(int argc, char** argv) {
int append = 1; int append = 1;
int crush_all = 0; int crush_all = 0;
int debug_mode = 0; int debug_mode = 0;
int allfiles = 0;
vector<string> testnames; vector<string> testnames;
int c; int c;
bool no_opt = true; bool no_opt = true;
while ((c = getopt(argc, argv, "hf:rdt:x")) != -1) { while ((c = getopt(argc, argv, "hf:ardt:x")) != -1) {
switch (c) { switch (c) {
case 'h': case 'h':
cout << "Usage: " << argv[0] << " -f <filename> [-r <truncate>] [-t <testnames>] [-h]\n"; cout << "Usage: " << argv[0] << " -f <filename> [-r <truncate>] [-t <testnames>] [-h]\n";
@@ -76,6 +102,9 @@ int main(int argc, char** argv) {
testnames.push_back(argv[optind] ); testnames.push_back(argv[optind] );
} }
break; break;
case 'a':
allfiles = 1;
break;
case 'r': case 'r':
append = 0; append = 0;
break; break;
@@ -101,310 +130,367 @@ int main(int argc, char** argv) {
cout << "\t\t-f => Specify .datalog file name\n"; cout << "\t\t-f => Specify .datalog file name\n";
cout << "\t\t-t => Specify tests to be extracted seprated by space\n"; cout << "\t\t-t => Specify tests to be extracted seprated by space\n";
cout << "\t\t-r => Truncate or replace generated files (default is append)\n"; cout << "\t\t-r => Truncate or replace generated files (default is append)\n";
cout << "\t\t-a => Process all .datalog files in current dir, overrides files specified in -f\n";
cout << "\t\t-x => Exract all tests, overrides tests specified in -t\n\n"; cout << "\t\t-x => Exract all tests, overrides tests specified in -t\n\n";
return 1; return 1;
} }
if (filename == "") {
return 0;
}
unordered_map<string, int> has_header; vector<string> datalog_files;
unordered_map<string, ofstream> FILE_HANDLE;
unordered_map<
string,
vector<vector<string>>
> hash_buff;
for(auto& name : testnames) { if (allfiles == 1 && filename == "") {
has_header[name] = 0; namespace fs = std::filesystem;
hash_buff[name].clear(); for (const auto& entry : fs::directory_iterator(fs::current_path())) {
if (append == 1) { if (entry.is_regular_file() && entry.path().extension() == ".datalog") {
FILE_HANDLE[name].open(name + ".csv", ios::app); datalog_files.push_back(entry.path().string());
} else { }
FILE_HANDLE[name].open(name + ".csv", ios::out | ios::trunc);
} }
} if (datalog_files.empty()) {
cerr << "No .datalog files found in current directory!\n";
ifstream file_input; return 1;
file_input = ifstream(filename);
stringstream buffer;
buffer << file_input.rdbuf();
vector <string> m_vec_file;
m_vec_file = tokenize(buffer.str(), "\n");
if (!file_input) {
cerr << "Could not open input file!\n";
return 1;
}
string testname = "";
string productname = "prodname";
string programrev = "99.99";
string operatorlotname = "operlotname";
string lotname = "0C00000";
string wafer = "99";
string temperature = "99";
string testsite;
string x_coord;
string y_coord;
unordered_map<string, int>
testcollookup = { {"TNAME", 9},
{"VCC", 10},
{"SUPPLIES", 11},
{"VALUE", 13},
{"INT", 14},
{"LOG_INT", 14},
{"RESULTS", 15},
{"PATTERN", 16},
{"STATE", 17},
{"NUM", 18},
{"CONDITION1", 19},
{"CONDITION2", 20},
{"CONDITION3", 21},
{"TEXT", 22}
};
int max_col = numeric_limits<int>::min();
for (const auto& value : testcollookup) {
if (value.second > max_col) {
max_col = value.second;
} }
} else {
if (filename == "") {
cerr << "No .datalog file specified!\n";
return 1;
}
datalog_files.push_back(filename);
} }
// You can now loop over datalog_files for processing
vector<std::future<void>> futures;
vector<string> testheader; int file_num = 1;
vector<string> testdetails;
string last_match;
string line;
auto time_start = steady_clock::now(); auto time_start = steady_clock::now();
long line_pos = 1; for (const auto& filename : datalog_files) {
long max_line_pos = m_vec_file.size(); int my_line = ++file_num;
string m_filename = (tokenize(filename, "\\")).back();
unsigned int part_size = m_vec_file.size() / 2; //thread_count;
// std::cout << "Part Size: " << part_size << " lines\n";
for (; part_size < m_vec_file.size(); ++part_size) {
vector<string> part_split_line;
string part_line = m_vec_file[part_size];
part_split_line = tokenize(part_line); futures.push_back(std::async(std::launch::async, [&, filename, my_line]() {
if (part_split_line[0] == "#START_DEVICE") {
break; if (filename == "") {
} return ;
}
} unordered_map<string, int> has_header;
// std::cout << m_vec_file[part_size] << "\n"; unordered_map<string, ofstream> FILE_HANDLE;
for (unsigned int ctr = 0; ctr < m_vec_file.size(); ++ctr) { unordered_map<
line = m_vec_file[ctr]; string,
if ((int(line_pos) % 500 == 0) || (line_pos == max_line_pos)) { vector<vector<string>>
std::cout << "\r File: " << m_filename << " [" << fixed << setprecision(3) << (100* (float)line_pos / (float)max_line_pos) << "%] "; > hash_buff;
}
vector<string> split_line;
string match1 = "99";
string match2 = "99";
split_line = tokenize(line); for(auto& name : testnames) {
has_header[name] = 0;
if (split_line.size()>0) { hash_buff[name].clear();
match1 = split_line.at(0); if (append == 1) {
FILE_HANDLE[name].open(name + ".csv", ios::app);
if (split_line.size()>1) {
match2 = split_line.at(1);
}
}
if (match1 == "#PRODUCT") {
productname = match2;
last_match = "PRODUCT";
} else if (match1 == "#REV") {
programrev = match2;
last_match = "REV";
} else if (match1 == "#OPER_LOT_ID") {
operatorlotname = match2;
last_match = "OPER_LOT_ID";
} else if (match1 == "#LOT") {
if (match2 != "99") {
string lotname_temp = match2;
if (lotname_temp.length() > 6) {
lotname = lotname_temp.substr(0, 7);
} else { } else {
lotname = lotname_temp; FILE_HANDLE[name].open(name + ".csv", ios::out | ios::trunc);
} }
last_match = "LOT";
} }
} else if (match1 == "#WAFER") {
wafer = match2;
last_match = "WAFER";
} else if (match1 == "#LOTWAFER") {
vector<string> lotwafer_info = tokenize(match2, "_");
wafer = lotwafer_info.at(2);
lotname = lotwafer_info.at(1);
last_match = "LOTWAFER";
} else if (match1 == "#TEMP") {
temperature = match2;
last_match = "TEMP";
} else if (match1 == "#START_DEVICE") {
last_match = "START_DEVICE"; ifstream file_input;
} else if (match1 == "#DIE_X") {
x_coord = match2; file_input = ifstream(filename);
last_match = "DIE_X"; stringstream buffer;
} else if (match1 == "#DIE_Y") { buffer << file_input.rdbuf();
y_coord = match2; vector <string> m_vec_file;
last_match = "DIE_Y"; m_vec_file = tokenize(buffer.str(), "\n");
} else if (match1 == "#SITE") {
testsite = match2;
last_match = "SITE";
} else if (match1 == "TNAME") {
stringstream ss(line);
string word;
testheader.clear();
while (ss >> word) { // Extract word from the stream.
testheader.push_back(word);
}
last_match = "TNAME";
} else if (match1 == "bin_result") { if (!file_input) {
// cout << "[" << match1 << "]\n"; cerr << "Could not open input file!\n";
for ( auto &my_pair : hash_buff ) { return ;
for (auto &m_arr : my_pair.second) { }
m_arr.push_back(split_line.at(3));
m_arr.push_back(split_line.at(2));
string testname = "";
string productname = "prodname";
string programrev = "99.99";
string operatorlotname = "operlotname";
string lotname = "0C00000";
string wafer = "99";
string temperature = "99";
string testsite;
string x_coord;
string y_coord;
unordered_map<string, int>
testcollookup = { {"TNAME", 9},
{"VCC", 10},
{"SUPPLIES", 11},
{"VALUE", 13},
{"INT", 14},
{"LOG_INT", 14},
{"RESULTS", 15},
{"PATTERN", 16},
{"STATE", 17},
{"NUM", 18},
{"CONDITION1", 19},
{"CONDITION2", 20},
{"CONDITION3", 21},
{"TEXT", 22}
};
int max_col = numeric_limits<int>::min();
for (const auto& value : testcollookup) {
if (value.second > max_col) {
max_col = value.second;
}
}
vector<string> testheader;
vector<string> testdetails;
string last_match;
string line;
long line_pos = 1;
long max_line_pos = m_vec_file.size();
string m_filename = (tokenize(filename, "\\")).back();
unsigned int part_size = m_vec_file.size() / 2; //thread_count;
// std::cout << "Part Size: " << part_size << " lines\n";
for (; part_size < m_vec_file.size(); ++part_size) {
vector<string> part_split_line;
string part_line = m_vec_file[part_size];
part_split_line = tokenize(part_line);
if (part_split_line[0] == "#START_DEVICE") {
break;
}
}
// std::cout << m_vec_file[part_size] << "\n";
for (unsigned int ctr = 0; ctr < m_vec_file.size(); ++ctr) {
line = m_vec_file[ctr];
if ((int(line_pos) % 100 == 0) || (line_pos == max_line_pos)) {
std::lock_guard<std::mutex> lock(cout_mutex); // Lock before updating console
int width = get_console_width();
std::ostringstream oss;
oss << " File: " << m_filename << " ";
std::string left = oss.str();
std::ostringstream progress_oss;
progress_oss << "[" << std::fixed << std::setprecision(3)
<< (100 * (float)line_pos / (float)max_line_pos) << "%]";
std::string right = progress_oss.str();
int padding = width - (int)left.length() - (int)right.length();
if (padding < 0) padding = 0;
set_cursor_position(my_line, 1);
std::cout << left << std::string(padding, ' ') << right << std::flush;
}
vector<string> split_line;
string match1 = "99";
string match2 = "99";
// istringstream iss(line);
// string word;
// while (iss >> word) split_line.push_back(word);
split_line = tokenize(line);
if (split_line.size()>0) {
match1 = split_line.at(0);
if (split_line.size()>1) {
match2 = split_line.at(1);
} }
}
} else if ( ( (crush_all == 1) && (match1!="TNAME") && (match1.size() > 0 && match1.at(0) != '#') ) || (find(testnames.begin(), testnames.end(), match1) != testnames.end()) ) {
stringstream ss(line);
string word;
testdetails.clear();
while (ss >> word) { // Extract word from the stream.
testdetails.push_back(word);
}
testname = testdetails[0];
vector<string> sub_buff;
for (int ctr = 0; ctr <= max_col; ++ctr) {
sub_buff.push_back(" ");
}
sub_buff[0] = productname;
sub_buff[1] = programrev;
sub_buff[2] = operatorlotname;
sub_buff[3] = lotname;
sub_buff[4] = wafer;
sub_buff[5] = x_coord;
sub_buff[6] = y_coord;
sub_buff[7] = testsite;
sub_buff[8] = temperature;
for (int i = 0; i <= (int)(testheader.size()-1); ++i) {
int index;
index = testcollookup[testheader[i]];
if (testheader[i] == "RESULTS") {
if (testdetails[i] == "PASS") {
sub_buff[index] = "1";
} else {
sub_buff[index] = "0";
} }
} else {
sub_buff[index] = testdetails[i];
}
}
if (!debug_mode) {
hash_buff[testname].push_back(sub_buff);
}
if (match1 == "#PRODUCT") {
} else if (match1 == "#END_DEVICE") { productname = match2;
if (!debug_mode) { last_match = "PRODUCT";
for ( auto &my_pair : hash_buff ) { } else if (match1 == "#REV") {
programrev = match2;
string test = my_pair.first; last_match = "REV";
string fileout = test + ".csv"; } else if (match1 == "#OPER_LOT_ID") {
operatorlotname = match2;
if (append ==1) { last_match = "OPER_LOT_ID";
} else if (match1 == "#LOT") {
if (match2 != "99") {
string lotname_temp = match2;
if (lotname_temp.length() > 6) {
lotname = lotname_temp.substr(0, 7);
} else {
lotname = lotname_temp;
}
last_match = "LOT";
}
} else if (match1 == "#WAFER") {
wafer = match2;
last_match = "WAFER";
} else if (match1 == "#LOTWAFER") {
vector<string> lotwafer_info = tokenize(match2, "_");
wafer = lotwafer_info.at(2);
lotname = lotwafer_info.at(1);
last_match = "LOTWAFER";
} else if (match1 == "#TEMP") {
temperature = match2;
last_match = "TEMP";
} else if (match1 == "#START_DEVICE") {
ifstream infile(fileout); last_match = "START_DEVICE";
if (infile.good()) } else if (match1 == "#DIE_X") {
{ x_coord = match2;
string sLine; last_match = "DIE_X";
} else if (match1 == "#DIE_Y") {
getline(infile, sLine); y_coord = match2;
last_match = "DIE_Y";
vector<string> sub_split_string = tokenize(sLine, ","); } else if (match1 == "#SITE") {
if (sub_split_string.at(0) == "productname") { testsite = match2;
has_header[test] = 1; last_match = "SITE";
} else if (match1 == "TNAME") {
stringstream ss(line);
string word;
testheader.clear();
while (ss >> word) { // Extract word from the stream.
testheader.push_back(word);
}
last_match = "TNAME";
} else if (match1 == "bin_result") {
// cout << "[" << match1 << "]\n";
for ( auto &my_pair : hash_buff ) {
for (auto &m_arr : my_pair.second) {
m_arr.push_back(split_line.at(3));
m_arr.push_back(split_line.at(2));
} }
}
infile.close();
} }
} else if ( ( (crush_all == 1) && (match1!="TNAME") && (match1.size() > 0 && match1.at(0) != '#') ) || (find(testnames.begin(), testnames.end(), match1) != testnames.end()) ) {
stringstream ss(line);
if (!has_header[test]) { string word;
if (!FILE_HANDLE[test] .is_open()) { testdetails.clear();
if (append == 1) { while (ss >> word) { // Extract word from the stream.
FILE_HANDLE[test].open(test + ".csv", ios::app); testdetails.push_back(word);
} else {
FILE_HANDLE[test].open(test + ".csv", ios::out | ios::trunc);
}
}
FILE_HANDLE[test] << "productname,programrev,operatorlotname,lotname,wafer,x,y,testsite,temperature,testname,vcc,vccio,vccaux,testdoublevalue,testintvalue,testpass,pattern,state,intval,condition1,condition2,condition3,text,bin,binname\n";
has_header[test] = 1;
}
for (auto &m_arr : hash_buff[test]) {
if (!FILE_HANDLE[test] .is_open()) {
FILE_HANDLE[test].open(test + ".csv", ios::app);
}
FILE_HANDLE[test] << join(m_arr,",") + "\n";
}
hash_buff[test].clear();
} }
testname = testdetails[0];
vector<string> sub_buff;
for (int ctr = 0; ctr <= max_col; ++ctr) {
sub_buff.push_back(" ");
}
sub_buff[0] = productname;
sub_buff[1] = programrev;
sub_buff[2] = operatorlotname;
sub_buff[3] = lotname;
sub_buff[4] = wafer;
sub_buff[5] = x_coord;
sub_buff[6] = y_coord;
sub_buff[7] = testsite;
sub_buff[8] = temperature;
for (int i = 0; i <= (int)(testheader.size()-1); ++i) {
int index;
index = testcollookup[testheader[i]];
if (testheader[i] == "RESULTS") {
if (testdetails[i] == "PASS") {
sub_buff[index] = "1";
} else {
sub_buff[index] = "0";
}
} else {
sub_buff[index] = testdetails[i];
}
}
if (!debug_mode) {
hash_buff[testname].push_back(sub_buff);
}
} else if (match1 == "#END_DEVICE") {
if (!debug_mode) {
for ( auto &my_pair : hash_buff ) {
string test = my_pair.first;
string fileout = test + ".csv";
if (append ==1) {
ifstream infile(fileout);
if (infile.good())
{
string sLine;
getline(infile, sLine);
vector<string> sub_split_string = tokenize(sLine, ",");
if (sub_split_string.at(0) == "productname") {
has_header[test] = 1;
}
}
infile.close();
}
if (!has_header[test]) {
if (!FILE_HANDLE[test] .is_open()) {
if (append == 1) {
FILE_HANDLE[test].open(test + ".csv", ios::app);
} else {
FILE_HANDLE[test].open(test + ".csv", ios::out | ios::trunc);
}
}
FILE_HANDLE[test].seekp(0, ios::end);
if (FILE_HANDLE[test].tellp() == 0) {
FILE_HANDLE[test] << "productname,programrev,operatorlotname,lotname,wafer,x,y,testsite,temperature,testname,vcc,vccio,vccaux,testdoublevalue,testintvalue,testpass,pattern,state,intval,condition1,condition2,condition3,text,bin,binname\n";
}
}
for (auto &m_arr : hash_buff[test]) {
if (!FILE_HANDLE[test] .is_open()) {
FILE_HANDLE[test].open(test + ".csv", ios::app);
}
FILE_HANDLE[test] << join(m_arr,",") + "\n";
}
hash_buff[test].clear();
}
}
}
++line_pos;
} }
}
for ( const auto &my_pair : hash_buff ) {
string test = my_pair.first;
// string fileout = test + ".csv";
++line_pos; // if (hash_buff[test].size() > 0) {
} // FILE_HANDLE[test] << hash_buff[test];
auto time_end = steady_clock::now(); // hash_buff[test].clear();
// }
cout << "\033[1;32mFinished: \033[0m" << duration_cast<milliseconds>(time_end - time_start).count() << " ms\n"; FILE_HANDLE[test].close();
}
for ( const auto &my_pair : hash_buff ) { }));
string test = my_pair.first;
// string fileout = test + ".csv";
// if (hash_buff[test].size() > 0) {
// FILE_HANDLE[test] << hash_buff[test];
// hash_buff[test].clear();
// }
FILE_HANDLE[test].close();
} }
for (auto& fut : futures) fut.get();
auto time_end = steady_clock::now();
cout << "\n\n\033[1;32m Finished: \033[0m" << duration_cast<milliseconds>(time_end - time_start).count() << " ms\n";
return 0; return 0;
} }
Binary file not shown.
BIN
View File
Binary file not shown.