diff --git a/CMakeLists.txt b/CMakeLists.txt index e7e937d08dfd1978232479cc2fcfe985f0f43987..a4dcefdefcbe4163f0978dd41d344231ba8a6e8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,31 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.1) + +project(apdBatchSetter) + +find_package(Qt5 COMPONENTS Core Gui Widgets Network Xml) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../proddb-clientlib/) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/includes/) LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/libs) -project(apdBatchSetter) -add_executable(${PROJECT_NAME} "main.cxx") -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ProductionDatabase boost_program_options) +add_library(toolbox "toolbox.cpp" "serialListReader.h" "serialListReader.cxx") +add_library(proddbaccess "proddb/ProdDbAccess.cxx" "proddb/ProdDbAccess.h" "proddb/ProductionDatabase.cxx" "proddb/ProductionDatabase.h") + +add_executable(apdBatchSetter "apdbatchsetter.cpp") +add_executable(apdLocationSetter "apdlocationsetter.cpp") +add_executable(apdBoxSetter "apdboxsetter.cpp") +add_executable(getLocations "getlocations.cpp") +add_executable(getU100 "getu100.cxx") +add_executable(makeSerialList "makeseriallist.cxx") + +target_link_libraries(toolbox Qt5::Network Qt5::Core Qt5::Xml proddbaccess) +target_link_libraries(proddbaccess Qt5::Network Qt5::Core Qt5::Xml) + +target_link_libraries(apdBatchSetter proddbclient boost_program_options) +target_link_libraries(apdBoxSetter proddbclient boost_program_options) +target_link_libraries(getLocations proddbaccess boost_program_options) +target_link_libraries(getU100 proddbaccess boost_program_options) +target_link_libraries(makeSerialList toolbox) + +target_link_libraries(apdLocationSetter proddbclient proddbaccess boost_program_options Qt5::Gui Qt5::Widgets) diff --git a/apdbatchsetter.cpp b/apdbatchsetter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b53083d1c6d5c13b20cdac706b1b6a030e8736f --- /dev/null +++ b/apdbatchsetter.cpp @@ -0,0 +1,138 @@ +#include <fstream> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> + +#include <productiondatabaseclient.h> +#include <boost/program_options.hpp> +#include "toolbox.cpp" + +using namespace std; +using namespace ProductionDatabase; +namespace po = boost::program_options; + +static bool irradiatedAPDs = false; +static string fileName = "serials.dat"; +static uint batchNo = 0; +static string username = ""; +static string password = ""; +static ProductionDatabaseClient * proddb; + +void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]); +po::options_description setupDescription(); +po::variables_map setupVariableMap(int argc, char* argv[], po::options_description desc); +void processVariablesAndQueryMissing(po::variables_map vm, po::options_description desc); +bool checkDatabaseConnectivityAndCredentials(); +void getCredentials(); +bool isResponseGood(DatabaseClientResponse response); +void getIrradiationStatus(string type); +void queryBatchNumber(); + +int main(int argc, char* argv[]) { + proddb = new ProductionDatabaseClient(); + processArgumentsAndQueryMissing(argc, argv); + vector<string> apdSerials = loadSerialsFromFileName(fileName); + if (!checkDatabaseConnectivityAndCredentials()) return 1; + proddb->assignBatchNumberToAPDs(apdSerials,batchNo,irradiatedAPDs); + cout << "\nSuccessfully assigned Batch " << batchNo << " to APDs from serial file " << fileName << "." << endl << endl; + return (-apdSerials.empty()); +} + +bool checkDatabaseConnectivityAndCredentials(){ + getCredentials(); + DatabaseClientResponse response = proddb->checkConnectivityAndCredentials(); + return isResponseGood(response); +} + +void getCredentials() { + if (username == "" || password == "") proddb->queryCredentials(); + else proddb->setCredentials(username, password); +} + +bool isResponseGood(DatabaseClientResponse response) { + if (response != Successful ) { + cerr << "Connection to database failed because of error: " << response << endl; + return false; + } + return true; +} + +void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) { + po::options_description desc = setupDescription(); + po::variables_map vm = setupVariableMap(m_argc, m_argv, desc); + processVariablesAndQueryMissing(vm, desc); +} + +po::options_description setupDescription() { + po::options_description desc("Available options"); + desc.add_options() + ("help", "produce help message") + ("type", po::value<string>(), "(required) type of APDs [new, irr]") + ("batch", po::value<int>(), "(required) batch number to be set") + ("fileName", po::value<string>(), "file name or serial numbers [serials.dat]") + ("user", po::value<string>(), "User name to connect to DB. Only used when combined with pass!") + ("pass", po::value<string>(), "Password to connect to DB. Only used when combined with user!") + ; + return desc; +} + +po::variables_map setupVariableMap(int argc, char* argv[], po::options_description desc) { + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + return vm; +} + +void processVariablesAndQueryMissing(po::variables_map vm, po::options_description desc) { + if (vm.count("help")) { + cout << desc << "\n"; + exit(0); + } + if (vm.count("batch")) { + batchNo = uint(vm["batch"].as<int>()); + } else { + queryBatchNumber(); + } + if (vm.count("fileName")) { + fileName = vm["fileName"].as<string>(); + cout << "Reading serials from " << fileName << endl; + } + if (vm.count("user")) { + username = vm["user"].as<string>(); + } + if (vm.count("pass")) { + password = vm["pass"].as<string>(); + } + if (vm.count("type")) { + getIrradiationStatus(vm["type"].as<string>()); + } else { + irradiatedAPDs = (batchNo >= 10000); + } +} + +void queryBatchNumber() { + cout << "Which Batch do these APDs belong to?" << endl; + try { + cin >> batchNo; + cout << endl; + } catch (...) { + batchNo = 0; + } + if (batchNo <= 0) { + cerr << "Invalid batch number!" << endl; + exit(-1); + } +} + +void getIrradiationStatus(string type) { + if (type == "new") { + irradiatedAPDs = false; + } + else if (type == "irr") { + irradiatedAPDs = true; + } else { + cerr << "Invalid type: " << type << "! Possible APD types: 'new' or 'irr'" << endl; + exit(0); + } +} diff --git a/main.cxx b/apdboxsetter.cpp similarity index 58% rename from main.cxx rename to apdboxsetter.cpp index 88c617cb66b72f26f8595a50d4c5b4a2a436ce69..5125d0d10a1c64e8d2d451e8069ad57b1efab4ee 100644 --- a/main.cxx +++ b/apdboxsetter.cpp @@ -2,53 +2,38 @@ #include <iostream> #include <iomanip> #include <string> - #include <vector> #include <productiondatabaseclient.h> #include <boost/program_options.hpp> +#include "toolbox.cpp" using namespace std; using namespace ProductionDatabase; namespace po = boost::program_options; static bool irradiatedAPDs = false; -static bool demoMode = false; static string fileName = "serials.dat"; -static uint batchNo = 0; +static uint boxNo = 0; static bool debug = false; +static string username = ""; +static string password = ""; -void readArguments(int m_argc, char* m_argv[]); +void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]); -int main(int argc, char* argv[]) -{ +int main(int argc, char* argv[]) { ProductionDatabaseClient *proddb = new ProductionDatabaseClient(); - - readArguments(argc, argv); + processArgumentsAndQueryMissing(argc, argv); if (debug) cout << "Intitialized apdBatchSetter with version " << proddb->getVersion() << " of the database access libraries." << endl << endl - << "Now trying to set Batch number " << batchNo << " for ADPs from serial file " << fileName << endl; + << "Now trying to set Batch number " << boxNo << " for ADPs from serial file " << fileName << endl; vector<string> apdSerials; - int nAPDs = 0; - - ifstream in(fileName.c_str()); - if (!in.good()) { - cerr << "Could not load serials. Does the input file exist?" << endl; - exit(0); - } - apdSerials.clear(); - while(in.good()) { - string newEntry = ""; - in >> newEntry; - if (!isdigit(newEntry[0])) continue; - nAPDs++; - apdSerials.push_back(newEntry); - if (debug) cout << "Found APD no. " << nAPDs << ": " << newEntry << endl; - } + apdSerials = loadSerialsFromFileName(fileName); - proddb->queryCredentials(); + if (username == "" || password == "") proddb->queryCredentials(); + else proddb->setCredentials(username, password); DatabaseClientResponse response = proddb->checkConnectivityAndCredentials(); if (response != Successful ) { cerr << "Connection to database failed because of error: " << response << endl; @@ -56,24 +41,22 @@ int main(int argc, char* argv[]) } if (debug) cout << "Connection successful!" << endl; - proddb->assignBatchNumberToAPDs(apdSerials,batchNo,irradiatedAPDs); - - cout << "\nSuccessfully assigned Batch " << batchNo << " to APDs from serial file " << fileName; - if (irradiatedAPDs) cout << " (after irradiation)"; - cout << "!\n\nThank you for using apdBatchSetter :)" << endl << endl; + cout << "\nBox " << boxNo << " was not assigned to APDs from serial file " << fileName << "because the corresponding field does not exist yet." + << "\nThank you for using apdBoxSetter! :)" << endl << endl; return (-apdSerials.empty()); } -void readArguments(int m_argc, char* m_argv[]) { +void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) { po::options_description desc("Available options"); desc.add_options() ("help", "produce help message") ("type", po::value<string>(), "(required) type of APDs [new, irr]") - ("batch", po::value<int>(), "(required) batch number to be set") + ("box", po::value<int>(), "(required) box number to be assigned") ("fileName", po::value<string>(), "file name or serial numbers [serials.dat]") - ("demo", "check connectivity without writing any data") ("debug", "emit additional messages for debugging purposes") + ("user", po::value<string>(), "User name to connect to DB. Only used when combined with pass!") + ("pass", po::value<string>(), "Password to connect to DB. Only used when combined with user!") ; po::variables_map vm; po::store(po::parse_command_line(m_argc, m_argv, desc), vm); @@ -109,36 +92,30 @@ void readArguments(int m_argc, char* m_argv[]) { exit(0); } } - if (vm.count("batch")) { - batchNo = uint(vm["batch"].as<int>()); + if (vm.count("box")) { + boxNo = uint(vm["batch"].as<int>()); } else { - cout << "Which Batch do these APDs belong to?" << endl; + cout << "Which box should these APDs be assigned to?" << endl; try { - cin >> batchNo; + cin >> boxNo; cout << endl; } catch (...) { - batchNo = 0; + boxNo = 0; } - if (batchNo <= 0) { + if (boxNo <= 0) { cerr << "Invalid batch number!" << endl; exit(-1); } - if (batchNo < 1000 && irradiatedAPDs) { - cerr << "Batches with irradiated APDs should have batch numbers greater than 10000!" << endl; - exit(-1); - } - if (batchNo >= 1000 && !irradiatedAPDs) { - cerr<< "Batches with batch numbers greather than 10000 have been irradiated!" << endl; - exit(-1); - } } if (vm.count("fileName")) { fileName = vm["fileName"].as<string>(); cout << "Reading serials from " << fileName << endl; } - if (vm.count("demo")) { - cout << "Running in demo mode. No data will be written!" << endl; - demoMode = true; + if (vm.count("user")) { + username = vm["user"].as<string>(); + } + if (vm.count("pass")) { + password = vm["pass"].as<string>(); } if (vm.count("debug")) { debug = true; diff --git a/apdlocationsetter.cpp b/apdlocationsetter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..724339acd8c1249bb33849441b09212208f10a99 --- /dev/null +++ b/apdlocationsetter.cpp @@ -0,0 +1,124 @@ +#include <fstream> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> + +#include <productiondatabaseclient.h> +#include <boost/program_options.hpp> +#include "toolbox.cpp" +#include "ProdDbAccess.h" + +#include "QtGui/QApplication" + +namespace po=boost::program_options; +using namespace std; +using namespace ProductionDatabase; + +static string fileName = "serials.dat"; +static string username = ""; +static string password = ""; +static string newLocation = ""; +static string change = ""; +static bool debug = false; +static bool unknownonly = false; + +void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) { + po::options_description desc("Available options"); + desc.add_options() + ("help", "produce help message") + ("fileName", po::value<string>(), "file name for serial numbers [serials.dat]") + ("user", po::value<string>(), "User name to connect to DB. Only used when combined with pass!") + ("pass", po::value<string>(), "Password to connect to DB. Only used when combined with user!") + ("location", po::value<string>(), "New Location of APDs") + ("debug", "emit additional messages for debugging purposes") + ("unknownOnly", "only work on APDs with location 'unknown' or 'Bochum'") + ("change", po::value<string>(), "Overwrite this even if 'unknownonly' is set.") + ; + + po::variables_map vm; + po::store(po::parse_command_line(m_argc, m_argv, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << desc << "\n"; + exit(0); + } + if (vm.count("fileName")) { + fileName = vm["fileName"].as<string>(); + cout << "Reading serials from " << fileName << endl; + } + if (vm.count("user")) { + username = vm["user"].as<string>(); + } + if (vm.count("pass")) { + password = vm["pass"].as<string>(); + } + if (vm.count("location")) { + newLocation = vm["location"].as<string>(); + } else { + cout << "Where are these APDs?" << endl; + try { + cin >> newLocation; + cout << endl; + } catch (...) { + newLocation = ""; + } + if (newLocation == "") { + cerr << "Invalid location!" << endl; + exit(-1); + } + } + if (vm.count("debug")) { + debug = true; + } + if (vm.count("unknownOnly")) { + unknownonly = true; + } + if (vm.count("change")) { + change = vm["change"].as<string>(); + } +} + +int main(int argc, char* argv[]) { + QApplication *_app = new QApplication(argc, argv); + + processArgumentsAndQueryMissing(argc, argv); + ProdDbAccess *_proddb = new ProdDbAccess(); + + vector<string> apdSerials; + vector<string> allApdSerials; + + allApdSerials = loadSerialsFromFileName(fileName); + apdSerials.clear(); + + for ( size_t i = 0 ; i < allApdSerials.size() ; i++ ) { + string existinglocation = _proddb->location( uint( stoul( allApdSerials[i] ) ) ); + if ( existinglocation != newLocation && ( !unknownonly || existinglocation == "unknown" || existinglocation == "Bochum" || existinglocation == change ) ) + apdSerials.push_back(allApdSerials[i]); + } + + if ( apdSerials.size() == 0 ) { + cout << "No data to write! Exiting." << endl; + return 0; + } + cout << "Data will be written for " << apdSerials.size() << " APDs." << endl; + + ProductionDatabaseClient *_proddbclient = new ProductionDatabaseClient(); + + if (username == "" || password == "") _proddbclient->queryCredentials(); + else _proddbclient->setCredentials(username, password); + DatabaseClientResponse response = _proddbclient->checkConnectivityAndCredentials(); + if (response != Successful ) { + cerr << "Connection to database failed because of error: " << response << endl; + return response; + } + if (debug) cout << "Connection successful!" << endl; + + _proddbclient->storeNewAPDLocation(apdSerials,newLocation); + + cout << "\nSuccessfully assigned Location '" << newLocation << "' to APDs from serial file " << fileName << "!\n\nThank you for using the apdLocationSetter :)" << endl << endl; + + _app->exit(0); + return (-apdSerials.empty()); +} diff --git a/getlocations.cpp b/getlocations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef7f5d2731942d428876299931603fa645b41a03 --- /dev/null +++ b/getlocations.cpp @@ -0,0 +1,41 @@ +// Basic +#include <fstream> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> + +// Boost +#include <boost/program_options.hpp> + +// Custom +#include "ProdDbAccess.h" +#include "toolbox.cpp" + +using namespace std; +namespace po = boost::program_options; + +static string fileName = "serials.dat"; + +int main(int argc, char* argv[]) { + if ( argc < 2 ) { + cerr << "ERROR: Missing parameters! Please provide the name of a text file containing the serials needed." << endl; + exit(1); + } + if ( argc > 2 ) { + cerr << "ERROR: Too many parameters! Please only provide the name of a text file containing the serials needed." << endl; + exit(1); + } + ProdDbAccess* _dba = new ProdDbAccess(); + + fileName = string(argv[1]); + + vector<string> apdSerials = loadSerialsFromFileName(fileName); + + for (size_t i = 0 ; i < apdSerials.size() ; i++ ) { + if (apdSerials[i].size() == 9) apdSerials[i] = "0" + apdSerials[i]; + cout << "S/N: " << apdSerials[i] << " '" << _dba->location( uint( stoul( apdSerials[i] ) ) ) << "'" << endl; + } + + return 0; +} diff --git a/getu100.cxx b/getu100.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8008cda2d5586be502cc0af50ff71ed6d5817e90 --- /dev/null +++ b/getu100.cxx @@ -0,0 +1,41 @@ +// Basic +#include <fstream> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> + +// Boost +#include <boost/program_options.hpp> + +// Custom +#include "ProdDbAccess.h" +#include "toolbox.cpp" + +using namespace std; +namespace po = boost::program_options; + +static string fileName = "serials.dat"; + +int main(int argc, char* argv[]) { + if ( argc < 2 ) { + cerr << "ERROR: Missing parameters! Please provide the name of a text file containing the serials needed." << endl; + exit(1); + } + if ( argc > 2 ) { + cerr << "ERROR: Too many parameters! Please only provide the name of a text file containing the serials needed." << endl; + exit(1); + } + ProdDbAccess* _dba = new ProdDbAccess(); + + fileName = string(argv[1]); + + vector<string> apdSerials = loadSerialsFromFileName(fileName); + + for (size_t i = 0 ; i < apdSerials.size() ; i++ ) { + if (apdSerials[i].size() == 9) apdSerials[i] = "0" + apdSerials[i]; + cout << apdSerials[i] << " " << _dba->vendorU100( uint( stoul( apdSerials[i] ) ) ) << endl; + } + + return 0; +} diff --git a/includes/AbsDbAccess.h b/includes/AbsDbAccess.h new file mode 120000 index 0000000000000000000000000000000000000000..f1e3e056afa188548db796ecb66eccf94c709ae5 --- /dev/null +++ b/includes/AbsDbAccess.h @@ -0,0 +1 @@ +/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/AbsDbAccess.h \ No newline at end of file diff --git a/includes/ProdDbAccess.h b/includes/ProdDbAccess.h new file mode 120000 index 0000000000000000000000000000000000000000..94b012d69563fc321d3d3b9bdf30bb5d8af6a62d --- /dev/null +++ b/includes/ProdDbAccess.h @@ -0,0 +1 @@ +/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProdDbAccess.h \ No newline at end of file diff --git a/includes/ProductionDatabase.h b/includes/ProductionDatabase.h new file mode 120000 index 0000000000000000000000000000000000000000..0aefe0e4778d1418c7060d24eebf6ba83745f502 --- /dev/null +++ b/includes/ProductionDatabase.h @@ -0,0 +1 @@ +/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProductionDatabase.h \ No newline at end of file diff --git a/makeseriallist.cxx b/makeseriallist.cxx new file mode 100644 index 0000000000000000000000000000000000000000..22332cdbcbbdc66fa7f68d20b971689e6cd21404 --- /dev/null +++ b/makeseriallist.cxx @@ -0,0 +1,38 @@ +#include "serialListReader.h" +#include "QtCore/QCoreApplication" +#include <vector> +#include <string> +#include <iostream> + +using namespace std; + +int main(int argc, char *argv[]) { + if (argc != 3) { + cerr << "Too few or too many parameters!" << endl; + return 1; + } + QCoreApplication *_app = new QCoreApplication(argc,argv); + serialListReader *_reader = new serialListReader(false); + + vector<string> serialListString = _reader->getFullList(); + vector<unsigned int> serialList; + serialList.clear(); + + for ( size_t i = 0 ; i < serialListString.size() ; i++) serialList.push_back( stoul( serialListString[i] ) ); + if ( serialListString.size() != serialList.size() ) { + cerr << "ERROR: Sizes don't match!" << endl; + return 1; + } + + unsigned int serialMin = stoul(argv[1]); + unsigned int serialMax = stoul(argv[2]); + int numOut = 0; + for ( size_t i = 0 ; i < serialList.size() ; i++) { + if ( serialList [i] >= serialMin && serialList [i] <= serialMax ) { + cout << serialListString[i] << endl; + numOut++; + } + } + cerr << "Found " << numOut << " APDs in the given range." << endl; + return 0; +} diff --git a/proddb/ProdDbAccess.cxx b/proddb/ProdDbAccess.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d173542553d4de209a467596e0bc641e626e3c78 --- /dev/null +++ b/proddb/ProdDbAccess.cxx @@ -0,0 +1,64 @@ +#include "ProdDbAccess.h" +#include "ProductionDatabase.h" + +#include <string> + +ProdDbAccess::ProdDbAccess() { + if (QCoreApplication::instance() == nullptr) { + char *argv[] = {nullptr}; + int argc = sizeof(argv) / sizeof(char*) - 1; + _app = new QCoreApplication(argc,argv); + } else { + _app = QCoreApplication::instance(); + } + _db = new proddb::ProductionDatabase(); +} + +ProdDbAccess::~ProdDbAccess() { + delete _app; + delete _db; +} + +ProdDbAccess::ProdDbAccess(ProdDbAccess& other) { + _app = other._app; + _db = other._db; +} + +double ProdDbAccess::vendorU100(unsigned int m_serial) { + return getDataForApd(m_serial).Gain100Voltage; +} + +double ProdDbAccess::vendorUbreakthrough(unsigned int m_serial) { + return getDataForApd(m_serial).BreakthroughVoltage; +} + +double ProdDbAccess::vendorDarkCurrent(unsigned int m_serial) { + return getDataForApd(m_serial).DarkCurrent; +} + +unsigned int ProdDbAccess::lotNumber(unsigned int m_serial) { + return m_serial / 100000000; +} + +unsigned int ProdDbAccess::waferNumber(unsigned int m_serial) { + return m_serial / 1000000 - m_serial / 100000000 * 100; +} + +std::string ProdDbAccess::location(unsigned int m_serial) { + return getDataForApd(m_serial).location; +} + +std::string ProdDbAccess::waferPosition(unsigned int m_serial) { + return getDataForApd(m_serial).wafer_position; +} + +proddb::DbApdData ProdDbAccess::getDataForApd(unsigned int m_serial) { + if (_cachedSN != m_serial) { + std::string serial = std::to_string(m_serial); + if (m_serial < 1000000000) serial = "0" + serial; + proddb::DbApdData newItem = _db->queryManufacturerApdData(serial); + _cache = newItem; + _cachedSN = m_serial; + } + return _cache; +} diff --git a/proddb/ProdDbAccess.h b/proddb/ProdDbAccess.h new file mode 100644 index 0000000000000000000000000000000000000000..87af93e018f11e172ef032a65a043be5678114dc --- /dev/null +++ b/proddb/ProdDbAccess.h @@ -0,0 +1,31 @@ +#pragma once + +#include "AbsDbAccess.h" +#include "ProductionDatabase.h" +#include <string> +#include "QtCore/QCoreApplication" + +class ProdDbAccess : public AbsDbAccess { + +public: + ProdDbAccess(); + ~ProdDbAccess(); + ProdDbAccess(ProdDbAccess& other); + + double vendorU100(unsigned int serial); + double vendorUbreakthrough(unsigned int serial); + double vendorDarkCurrent(unsigned int serial); + + unsigned int lotNumber(unsigned int serial); + unsigned int waferNumber(unsigned int serial); + std::string waferPosition(unsigned int serial); + + std::string location(unsigned int serial); + +private: + proddb::ProductionDatabase *_db; + proddb::DbApdData _cache; + QCoreApplication *_app; + unsigned int _cachedSN = 0; + proddb::DbApdData getDataForApd(unsigned int serial); +}; diff --git a/proddb/ProductionDatabase.cxx b/proddb/ProductionDatabase.cxx new file mode 100644 index 0000000000000000000000000000000000000000..715ee0d00f07c1076a06ec91bba33dc5818f218c --- /dev/null +++ b/proddb/ProductionDatabase.cxx @@ -0,0 +1,169 @@ +#include "ProductionDatabase.h" + +#include <iostream> +#include <limits> +#include <stdexcept> +#include <unistd.h> + +#include <QtXml/QDomDocument> +#include <QtXml/QDomNode> +#include <QtXml/QDomNodeList> +#include <QtNetwork/QNetworkRequest> +#include <QtNetwork/QNetworkReply> +#include <QtCore/QUrl> +#include <QtNetwork/QNetworkAccessManager> + +using namespace proddb; + +ProductionDatabase::ProductionDatabase () + : _netman ( new QNetworkAccessManager ( this ) ) + , _apdDetailsApiUrl ( QString::fromUtf8 ( "https://ep1.ruhr-uni-bochum.de/endcapProductionDB/api/apd_details.php?serial=%1" ) ) + , _debug ( false ) { + checkDebugMode(); +} + +ProductionDatabase::~ProductionDatabase() { + delete _netman; +} + +void ProductionDatabase::checkDebugMode() { + const char*const envvar = getenv ( "PRODUCTIONDATABASE" ); + if ( envvar != nullptr ) { + if ( std::string ( envvar ) == std::string ( "DEBUG" ) ) + _debug = true; + } +} + +DbApdData ProductionDatabase::queryManufacturerApdData ( const std::string& serial ) { + if ( serial.size() == 0 ) { + const DbApdData data = { + false, + std::numeric_limits< double >::quiet_NaN(), + std::numeric_limits< double >::quiet_NaN(), + std::numeric_limits< double >::quiet_NaN(), + "", + "" + }; + return data; + } + try { + if ( _debug ) + std::cout << "Initiating network request to " << _apdDetailsApiUrl.arg ( serial.c_str() ).toUtf8().constData() << std::endl; + QNetworkReply* apddetails = _netman->get ( QNetworkRequest ( QUrl ( _apdDetailsApiUrl.arg ( serial.c_str() ) ) ) ); + connect ( apddetails, SIGNAL ( finished() ), &_waitForNetwork, SLOT ( quit() ) ); + _waitForNetwork.exec(); + if ( _debug ) + std::cout << "Done, received reply." << std::endl; + + if ( apddetails->attribute ( QNetworkRequest::HttpStatusCodeAttribute ).toInt() != 200 ) { + const QString exceptionMsg = QString::fromUtf8 ( "HTTP error while invoking Production DB Web API: The server returned HTTP status code %1." ).arg ( apddetails->attribute ( QNetworkRequest::HttpStatusCodeAttribute ).toInt() ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + QDomDocument dom; + QString errorMsg; + int errorLine; + int errorColumn; + + if ( _debug ) + std::cout << "Setting content of network reply to QDomDocument, verifying response..." << std::endl; + if ( !dom.setContent ( apddetails, &errorMsg, &errorLine, &errorColumn ) ) { + const QString exceptionMsg = QString::fromUtf8 ( "Error in parsing data for APD %4: %1 (line %2, column %3)" ).arg ( errorMsg ).arg ( errorLine ).arg ( errorColumn ).arg ( serial.c_str() ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + if ( dom.documentElement().nodeName() != QString::fromUtf8 ( "productiondb" ) + || dom.documentElement().firstChild().nodeName() != QString::fromUtf8 ( "request" ) ) + throw std::runtime_error ( "APD data has invalid format." ); + + QDomNode requestResult = dom.documentElement().firstChild(); + if ( !requestResult.attributes().contains ( QString::fromUtf8 ( "successful" ) ) + || !requestResult.attributes().contains ( QString::fromUtf8 ( "error_number" ) ) ) + throw std::runtime_error ( "APD data has invalid format." ); + + if ( requestResult.attributes().namedItem ( QString::fromUtf8 ( "successful" ) ).nodeValue() != + QString::fromUtf8 ( "true" ) ) { + const QString exceptionMsg = QString::fromUtf8 ( "Server reports error %1 when querying data of APD %2:\n%3" ).arg ( requestResult.attributes().namedItem ( QString::fromUtf8 ( "error_number" ) ).nodeValue() ).arg ( serial.c_str() ).arg ( requestResult.nextSibling().firstChild().nodeValue() ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + if ( _debug ) + std::cout << "Response OK, extracting data..." << std::endl; + + const QDomNodeList nodeListBreakthrough = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "breakthrough_voltage" ) ); + if ( nodeListBreakthrough.length() != 1 ) + throw std::runtime_error ( "Tag <breakthrough_voltage> missing or occuring more than once." ); + const QDomNode breakthroughNode = nodeListBreakthrough.item ( 0 ); + + bool ok = false; + const double breakthroughVoltage = breakthroughNode.firstChild().nodeValue().toDouble ( &ok ); + if ( !ok ) + throw std::runtime_error ( "Error parsing break-through voltage value into double." ); + + const QDomNodeList nodeListGain100 = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "gain100_voltage" ) ); + if ( nodeListGain100.length() != 1 ) + throw std::runtime_error ( "Tag <gain100_voltage> missing or occuring more than once." ); + const QDomNode gain100Node = nodeListGain100.item ( 0 ); + + ok = false; + const double gain100Voltage = gain100Node.firstChild().nodeValue().toDouble ( &ok ); + if ( !ok ) + throw std::runtime_error ( "Error parsing gain 100 voltage value into double." ); + + const QDomNodeList nodeListDarkCurrent = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "darkcurrent" ) ); + if ( nodeListDarkCurrent.length() != 1 ) + throw std::runtime_error ( "Tag <darkcurrent> missing or occuring more than once." ); + const QDomNode darkCurrentNode = nodeListDarkCurrent.item ( 0 ); + + ok = false; + const double darkCurrent = darkCurrentNode.firstChild().nodeValue().toDouble ( &ok ) / 1e9; // Dark current stored as Nanoampere in the database + if ( !ok ) + throw std::runtime_error ( "Error parsing dark current value into double." ); + + const QDomNodeList nodeListWaferPos = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "wafer_position" ) ); + if ( nodeListWaferPos.length() != 1 ) + throw std::runtime_error ( "Tag <wafer_position> missing or occuring more than once." ); + const QDomNode waferPosNode = nodeListWaferPos.item ( 0 ); + + ok = false; + const std::string waferPos = waferPosNode.firstChild().nodeValue().toStdString(); + if (waferPos.size() == 3) ok = true; + if ( !ok ) + throw std::runtime_error ( "Error reading wafer position" ); + + const QDomNodeList nodeListLocation = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "location" ) ); + if ( nodeListLocation.length() != 1 ) + throw std::runtime_error ( "Tag <location> missing or occuring more than once." ); + const QDomNode locationNode = nodeListLocation.item ( 0 ); + + ok = false; + const std::string location = locationNode.firstChild().nodeValue().toStdString(); + if (location.size() > 0) ok = true; + if ( !ok ) + throw std::runtime_error ( "Error reading location" ); + + if ( _debug ) + std::cout << "Data extraction complete, got " + << "Break-through voltage " << breakthroughVoltage << " V, " + << "Gain 100 voltage " << gain100Voltage << " V (at room temperature), " + << "Dark current " << darkCurrent << " A" + << "Position in wafer " << waferPos + << "Location " << location + << std::endl; + + const DbApdData data = {true, breakthroughVoltage, gain100Voltage, darkCurrent, waferPos, location }; + return data; + } + catch ( std::exception& e ) { + std::cerr << e.what() << std::endl; + const DbApdData data = { + false, + std::numeric_limits< double >::quiet_NaN(), + std::numeric_limits< double >::quiet_NaN(), + std::numeric_limits< double >::quiet_NaN(), + "", + "" + }; + return data; + } +} diff --git a/proddb/ProductionDatabase.h b/proddb/ProductionDatabase.h new file mode 100644 index 0000000000000000000000000000000000000000..40f92540d926f5217dc7b6c69e4672d017a51390 --- /dev/null +++ b/proddb/ProductionDatabase.h @@ -0,0 +1,37 @@ +#pragma once + +#include <QtCore/QObject> +#include <QtCore/QEventLoop> +#include <string> + +class QNetworkAccessManager; + +namespace proddb { + + struct DbApdData { + bool foundApd; + double BreakthroughVoltage; + double Gain100Voltage; + double DarkCurrent; + std::string wafer_position; + std::string location; + }; + + class ProductionDatabase : public QObject { + private: + QNetworkAccessManager* _netman; + QEventLoop _waitForNetwork; + const QString _apdDetailsApiUrl; + bool _debug; + + void checkDebugMode(); + public: + ProductionDatabase (); + ~ProductionDatabase(); + ProductionDatabase ( const ProductionDatabase& other ) = delete; + ProductionDatabase ( ProductionDatabase&& other ) = delete; + ProductionDatabase& operator= ( const ProductionDatabase& other ) = delete; + ProductionDatabase& operator= ( ProductionDatabase&& other ) = delete; + DbApdData queryManufacturerApdData(const std::string& serial); + }; +} diff --git a/serialListReader.cxx b/serialListReader.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4dde2e5a0d14ee262b734b24d36095bdf7de286a --- /dev/null +++ b/serialListReader.cxx @@ -0,0 +1,88 @@ +#include "serialListReader.h" + +#include <iostream> +#include <limits> +#include <stdexcept> +#include <unistd.h> + +#include <QtXml/QDomDocument> +#include <QtXml/QDomNode> +#include <QtXml/QDomNodeList> +#include <QtNetwork/QNetworkRequest> +#include <QtNetwork/QNetworkReply> +#include <QtCore/QUrl> +#include <QtNetwork/QNetworkAccessManager> + +serialListReader::serialListReader(bool debug = false) + : _netman ( new QNetworkAccessManager ( this ) ) { + _debug = debug; +} + +std::vector<std::string> serialListReader::getFullList() { + try { + if ( _debug ) + std::cout << "Initiating network request to https://ep1.ruhr-uni-bochum.de/endcapProductionDB/api/apd_details_listall.php" << std::endl; + QNetworkReply* apddetails = _netman->get ( QNetworkRequest ( QUrl ( "https://ep1.ruhr-uni-bochum.de/endcapProductionDB/api/apd_details_listall.php" ) ) ); + connect ( apddetails, SIGNAL ( finished() ), &_waitForNetwork, SLOT ( quit() ) ); + _waitForNetwork.exec(); + if ( _debug ) + std::cout << "Done, received reply." << std::endl; + + if ( apddetails->attribute ( QNetworkRequest::HttpStatusCodeAttribute ).toInt() != 200 ) { + const QString exceptionMsg = QString::fromUtf8 ( "HTTP error while invoking Production DB Web API: The server returned HTTP status code %1." ).arg ( apddetails->attribute ( QNetworkRequest::HttpStatusCodeAttribute ).toInt() ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + QDomDocument dom; + QString errorMsg; + int errorLine; + int errorColumn; + + if ( _debug ) + std::cout << "Setting content of network reply to QDomDocument, verifying response..." << std::endl; + if ( !dom.setContent ( apddetails, &errorMsg, &errorLine, &errorColumn ) ) { + const QString exceptionMsg = QString::fromUtf8 ( "Error in parsing data" ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + if ( dom.documentElement().nodeName() != QString::fromUtf8 ( "productiondb" ) + || dom.documentElement().firstChild().nodeName() != QString::fromUtf8 ( "request" ) ) + throw std::runtime_error ( "APD data has invalid format." ); + + QDomNode requestResult = dom.documentElement().firstChild(); + if ( !requestResult.attributes().contains ( QString::fromUtf8 ( "successful" ) ) + || !requestResult.attributes().contains ( QString::fromUtf8 ( "error_number" ) ) ) + throw std::runtime_error ( "APD data has invalid format." ); + + if ( requestResult.attributes().namedItem ( QString::fromUtf8 ( "successful" ) ).nodeValue() != + QString::fromUtf8 ( "true" ) ) { + const QString exceptionMsg = QString::fromUtf8 ( "Server reports error %1 when querying data" ); + throw std::runtime_error ( exceptionMsg.toUtf8().constData() ); + } + + if ( _debug ) + std::cout << "Response OK, extracting data..." << std::endl; + + const QDomNodeList nodeListApds = dom.documentElement().elementsByTagName ( QString::fromUtf8 ( "apd" ) ); + if ( _debug ) + std::cout << "Got " << nodeListApds.length() << " APDs!" << std::endl; + + std::vector<std::string> serialList; + serialList.clear(); + for ( size_t i = 0; i < nodeListApds.length(); i++ ) { + QDomNode apdNode = nodeListApds.item ( i ); + std::string serial = apdNode.attributes().namedItem ( QString::fromUtf8 ( "serial" ) ).nodeValue().toStdString(); + serialList.push_back(serial); + } + + if ( _debug ) + std::cout << "Returning serial list with " << serialList.size() << " entries!" << std::endl; + return serialList; + } + catch ( std::exception& e ) { + std::cerr << e.what() << std::endl; + std::vector<std::string> serialList; + serialList.clear(); + return serialList; + } +} diff --git a/serialListReader.h b/serialListReader.h new file mode 100644 index 0000000000000000000000000000000000000000..c921fe0e6060f6976308081ae42fc246a531335f --- /dev/null +++ b/serialListReader.h @@ -0,0 +1,21 @@ +#pragma once + +#include <vector> +#include <QtCore/QObject> +#include <QtCore/QEventLoop> + +class QNetworkAccessManager; + +class serialListReader : public QObject { + +private: + bool _debug = true; + QNetworkAccessManager* _netman; + QEventLoop _waitForNetwork; + const QString _apdDetailsApiUrl; + +public: + serialListReader(bool debug); + std::vector<std::string> getFullList(); +}; + diff --git a/toolbox.cpp b/toolbox.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d3f5ad3f952a0c9758c2b9105b10c00749c42da --- /dev/null +++ b/toolbox.cpp @@ -0,0 +1,29 @@ +#include <fstream> +#include <iostream> +#include <string> +#include <vector> + +using namespace std; + +vector<string> loadSerialsFromFileName(string m_fileName, bool m_debug = false) { + ifstream in(m_fileName.c_str()); + if (!in.good()) { + cerr << "Could not load serials. Does the input file exist?" << endl; + exit(0); + } + + vector<string> result; + result.clear(); + + string line = ""; + while (in.good()) { + getline(in,line); + if (line.length() == 0 || !isdigit(line[0])) continue; + result.push_back(line); + if (m_debug) cout << "Found serial: " << line << endl; + } + cerr << "Found " << result.size() << " APDs" << endl; + return result; +} + +