#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace std::chrono; std::mutex cout_mutex; 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 const &strings, string delim) { stringstream ss; string res = ""; copy(strings.begin(), strings.end(), ostream_iterator(ss, delim.c_str())); res = ss.str(); res.pop_back(); return res; } inline vector tokenize(string s, string delimiter = " ") { size_t pos_start = 0, pos_end, delim_len = delimiter.length(); string token; vector res; while ((pos_end = s.find(delimiter, pos_start)) != string::npos) { token = s.substr (pos_start, pos_end - pos_start); pos_start = pos_end + delim_len; res.push_back (token); } res.push_back (s.substr (pos_start)); return res; } int main(int argc, char** argv) { system("cls"); ios::sync_with_stdio(false); cin.tie(NULL); //may return 0 when not able to detect // const unsigned int max_thread_count = std::thread::hardware_concurrency(); // unsigned int thread_count = max_thread_count - 1; string filename = ""; int append = 1; int crush_all = 0; int debug_mode = 0; int allfiles = 0; vector testnames; int c; bool no_opt = true; while ((c = getopt(argc, argv, "hf:ardt:x")) != -1) { switch (c) { case 'h': cout << "Usage: " << argv[0] << " -f [-r ] [-t ] [-h]\n"; return 0; case 'f': filename = optarg; break; case 't': optind--; for( ;optind < argc && *argv[optind] != '-'; optind++){ testnames.push_back(argv[optind] ); } break; case 'a': allfiles = 1; break; case 'r': append = 0; break; case 'x': crush_all = 1; break; case 'd': debug_mode = 1; break; default: cout << "Invalid option. Use -h for help.\n"; return 1; } no_opt = false; } if (no_opt == true) { vector prog_path = tokenize(argv[0], "\\"); string prog_name = prog_path.back(); cout << "\nUsage: " << prog_name << " -f [-r ] [-t ] [-h]\n"; cout << "\tOptions:\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-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"; return 1; } vector datalog_files; if (allfiles == 1 && filename == "") { namespace fs = std::filesystem; for (const auto& entry : fs::directory_iterator(fs::current_path())) { if (entry.is_regular_file() && entry.path().extension() == ".datalog") { datalog_files.push_back(entry.path().string()); } } if (datalog_files.empty()) { cerr << "No .datalog files found in current directory!\n"; return 1; } } 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> futures; int file_num = 1; auto time_start = steady_clock::now(); for (const auto& filename : datalog_files) { int my_line = ++file_num; futures.push_back(std::async(std::launch::async, [&, filename, my_line]() { if (filename == "") { return ; } unordered_map has_header; unordered_map FILE_HANDLE; unordered_map< string, vector> > hash_buff; for(auto& name : testnames) { has_header[name] = 0; hash_buff[name].clear(); if (append == 1) { FILE_HANDLE[name].open(name + ".csv", ios::app); } else { FILE_HANDLE[name].open(name + ".csv", ios::out | ios::trunc); } } ifstream file_input; file_input = ifstream(filename); stringstream buffer; buffer << file_input.rdbuf(); vector m_vec_file; m_vec_file = tokenize(buffer.str(), "\n"); if (!file_input) { cerr << "Could not open input file!\n"; return ; } 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 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::min(); for (const auto& value : testcollookup) { if (value.second > max_col) { max_col = value.second; } } vector testheader; vector 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 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 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 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); } } 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 { lotname = lotname_temp; } last_match = "LOT"; } } else if (match1 == "#WAFER") { wafer = match2; last_match = "WAFER"; } else if (match1 == "#LOTWAFER") { vector 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"; } else if (match1 == "#DIE_X") { x_coord = match2; last_match = "DIE_X"; } else if (match1 == "#DIE_Y") { y_coord = match2; last_match = "DIE_Y"; } 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") { // 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)); } } } 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 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 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"; // 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(time_end - time_start).count() << " ms\n"; return 0; }