From 4b7743fdebf573ae22c6fda280b4c77b61a9339a Mon Sep 17 00:00:00 2001
From: Jan Reher <jreher@ep1.rub.de>
Date: Wed, 15 Jan 2020 14:56:16 +0100
Subject: [PATCH] Several changes that should all have been added to git long
 ago.

---
 CMakeLists.txt           |  22 +++----
 apdbatchsetter.cpp       |  13 ++--
 apdboxsetter.cpp         |  85 +++++++++++++++-----------
 apdlocationsetter.cpp    |   7 ++-
 getbatch.cpp             |   3 +-
 getirradiationstatus.cxx |   3 +-
 getlocations.cpp         |   5 +-
 getu100.cxx              |   3 +-
 gridListReader.cxx       | 125 +++++++++++++++++++++++++++++++++++++++
 gridListReader.h         |  41 +++++++++++++
 makeGridList.cxx         |  83 ++++++++++++++++++++++++++
 testxmlstructure.cxx     |  39 +++++++++---
 toolbox.cpp              |  18 +++++-
 13 files changed, 380 insertions(+), 67 deletions(-)
 create mode 100644 gridListReader.cxx
 create mode 100644 gridListReader.h
 create mode 100644 makeGridList.cxx

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd1151d..8642118 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/proddb/)
 INCLUDE_DIRECTORIES($ENV{proddb_clientlib_includes})
 LINK_DIRECTORIES($ENV{proddb_clientlib_libpath})
 
-add_library(toolbox "toolbox.cpp" "serialListReader.h" "serialListReader.cxx")
+add_library(toolbox "toolbox.cpp" "serialListReader.h" "serialListReader.cxx" "gridListReader.h" "gridListReader.cxx")
 add_library(proddbaccess "proddb/ProdDbAccess.cxx" "proddb/ProdDbAccess.h" "proddb/ProductionDatabase.cxx" "proddb/ProductionDatabase.h" "proddb/ProductionDatabaseApdStorage.cxx" "proddb/ProductionDatabaseApdStorage.h" "proddb/BulkDbAccess.h" "proddb/BulkDbAccess.cxx")
 
 add_executable(apdBatchSetter "apdbatchsetter.cpp")
@@ -23,6 +23,7 @@ add_executable(getLocations "getlocations.cpp")
 add_executable(getBatch "getbatch.cpp")
 add_executable(getU100 "getu100.cxx")
 add_executable(makeSerialList "makeseriallist.cxx")
+add_executable(makeGridList "makeGridList.cxx")
 add_executable(getIrradiationDose "getirradiationstatus.cxx")
 
 add_executable(testXmlStructure "testxmlstructure.cxx")
@@ -30,18 +31,19 @@ add_executable(testXmlStructure "testxmlstructure.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(apdBatchSetter proddbaccess proddbclient boost_program_options)
+target_link_libraries(apdBoxSetter proddbaccess proddbclient boost_program_options)
 target_link_libraries(getLocations proddbaccess boost_program_options)
 target_link_libraries(getBatch proddbaccess boost_program_options)
 target_link_libraries(getIrradiationDose proddbaccess boost_program_options)
 target_link_libraries(getU100 proddbaccess boost_program_options)
 target_link_libraries(makeSerialList toolbox)
-target_link_libraries(testXmlStructure Qt5::Core Qt5::Xml)
-
-target_link_libraries(apdLocationSetter proddbclient proddbaccess boost_program_options Qt5::Gui Qt5::Widgets)
-target_link_libraries(setArrivalForIrradiation proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
-target_link_libraries(setSentForAnalysisAfterIrradiation proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
-target_link_libraries(setIrradiationInfo proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
-target_link_libraries(setAnnealingInfo proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
+target_link_libraries(makeGridList toolbox)
+target_link_libraries(testXmlStructure proddbclient Qt5::Core Qt5::Xml)
+
+target_link_libraries(apdLocationSetter proddbaccess proddbclient proddbaccess boost_program_options Qt5::Gui Qt5::Widgets)
+target_link_libraries(setArrivalForIrradiation proddbaccess proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
+target_link_libraries(setSentForAnalysisAfterIrradiation proddbaccess proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
+target_link_libraries(setIrradiationInfo proddbaccess proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
+target_link_libraries(setAnnealingInfo proddbaccess proddbclient boost_program_options Qt5::Gui Qt5::Widgets)
 
diff --git a/apdbatchsetter.cpp b/apdbatchsetter.cpp
index 8b53083..898cf13 100644
--- a/apdbatchsetter.cpp
+++ b/apdbatchsetter.cpp
@@ -17,7 +17,7 @@ static string fileName = "serials.dat";
 static uint batchNo = 0;
 static string username = "";
 static string password = "";
-static ProductionDatabaseClient * proddb;
+static ProductionDatabaseClient *dbClient;
 
 void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]);
 po::options_description setupDescription();
@@ -30,24 +30,24 @@ void getIrradiationStatus(string type);
 void queryBatchNumber();
 
 int main(int argc, char* argv[]) {
-  proddb = new ProductionDatabaseClient();
+  dbClient = new ProductionDatabaseClient();
   processArgumentsAndQueryMissing(argc, argv);
   vector<string> apdSerials = loadSerialsFromFileName(fileName);
   if (!checkDatabaseConnectivityAndCredentials()) return 1;
-  proddb->assignBatchNumberToAPDs(apdSerials,batchNo,irradiatedAPDs);
+  dbClient->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();
+  DatabaseClientResponse response = dbClient->checkConnectivityAndCredentials();
   return isResponseGood(response);
 }
 
 void getCredentials() {
-  if (username == "" || password == "") proddb->queryCredentials();
-  else proddb->setCredentials(username, password);
+  if (username == "" || password == "") dbClient->queryCredentials();
+  else dbClient->setCredentials(username, password);
 }
 
 bool isResponseGood(DatabaseClientResponse response) {
@@ -92,6 +92,7 @@ void processVariablesAndQueryMissing(po::variables_map vm, po::options_descripti
   if (vm.count("batch")) {
     batchNo = uint(vm["batch"].as<int>());
   } else {
+
     queryBatchNumber();
   }
   if (vm.count("fileName")) {
diff --git a/apdboxsetter.cpp b/apdboxsetter.cpp
index 5125d0d..4574470 100644
--- a/apdboxsetter.cpp
+++ b/apdboxsetter.cpp
@@ -6,31 +6,32 @@
 
 #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 boxNo = 0;
+static int boxNo = 0;
 static bool debug = false;
 static string username = "";
 static string password = "";
 
+static vector<string> apdSerials;
+static std::vector<int> boxes;
+static std::vector<int> positions;
+
 void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]);
+void loadSerialsFromFileName(string m_fileName, bool m_debug = false);
 
 int main(int argc, char* argv[]) {
     ProductionDatabaseClient *proddb = new ProductionDatabaseClient();
     processArgumentsAndQueryMissing(argc, argv);
 
-    if (debug) cout << "Intitialized apdBatchSetter with version " << proddb->getVersion() << " of the database access libraries." << endl << endl
+    if (debug) cout << "Intitialized apdBoxSetter with version " << proddb->getVersion() << " of the database access libraries." << endl << endl
                     << "Now trying to set Batch number " << boxNo << " for ADPs from serial file " << fileName << endl;
 
-    vector<string> apdSerials;
-
-    apdSerials = loadSerialsFromFileName(fileName);
+    loadSerialsFromFileName(fileName);
 
     if (username == "" || password == "") proddb->queryCredentials();
     else proddb->setCredentials(username, password);
@@ -41,7 +42,9 @@ int main(int argc, char* argv[]) {
     }
     if (debug) cout << "Connection successful!" << endl;
 
-    cout << "\nBox " << boxNo << " was not assigned to APDs from serial file " << fileName << "because the corresponding field does not exist yet."
+    proddb->storeApdBoxNumber(apdSerials,boxes,positions);
+
+    cout << "\nBox " << boxNo << " was successfully assigned to APDs from serial file " << fileName << ". At least I hope so. In any case, something happened for " << apdSerials.size() << " APDs."
          << "\nThank you for using apdBoxSetter! :)" << endl << endl;
 
     return (-apdSerials.empty());
@@ -66,34 +69,8 @@ void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) {
         cout << desc << "\n";
         exit(0);
     }
-    if (vm.count("type")) {
-        if (vm["type"].as<string>() == "new") {
-            irradiatedAPDs = false;
-        }
-        else if (vm["type"].as<string>() == "irr") {
-            irradiatedAPDs = true;
-        } else {
-            cerr << "Invalid type! Possible APD types: 'new' or 'irr'" << endl;
-            exit(0);
-        }
-    } else {
-        string temp = "";
-        cout << "Have these APDs been irradiated? (yes / no)" << endl;
-        cin >> temp;
-        cout << endl;
-        if (temp == "yes" || temp == "y") {
-            irradiatedAPDs = true;
-        }
-        else if (temp == "no" || temp == "n") {
-            irradiatedAPDs = false;
-        }
-        else {
-            cerr << "Come on, you can't even give me a straight answer on that one? I'm sick of this. I'm leaving. Try again when you have the information you need." << endl;
-            exit(0);
-        }
-    }
     if (vm.count("box")) {
-        boxNo = uint(vm["batch"].as<int>());
+        boxNo = int(vm["box"].as<int>());
     } else {
         cout << "Which box should these APDs be assigned to?" << endl;
         try {
@@ -121,3 +98,41 @@ void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) {
         debug = true;
     }
 }
+
+void loadSerialsFromFileName(string m_fileName, bool m_debug) {
+    ifstream in(m_fileName.c_str());
+    if (!in.good()) {
+        cerr << "Could not load serials. Does the input file exist?" << endl;
+        exit(0);
+    }
+
+    apdSerials.clear();
+    positions.clear();
+    boxes.clear();
+
+    int pos = 1;
+
+    string line = "";
+    while (in.good()) {
+        getline(in,line);
+        if (line.length() == 0 || !isdigit(line[0])) {
+          pos++;
+          continue;
+        }
+        apdSerials.push_back(line);
+        boxes.push_back(boxNo);
+        positions.push_back(pos++);
+        if (m_debug) cout << "Found serial: " << line << endl;
+    }
+    if (apdSerials.size() != positions.size() || apdSerials.size() != boxes.size()) {
+      cerr << "ERROR: Array sizes don't match!" << endl;
+      apdSerials.clear();
+      boxes.clear();
+      positions.clear();
+      return;
+    }
+    cerr << "Found " << apdSerials.size() << " APDs" << endl;
+    return;
+}
+
+
diff --git a/apdlocationsetter.cpp b/apdlocationsetter.cpp
index 8c29d36..876d5db 100644
--- a/apdlocationsetter.cpp
+++ b/apdlocationsetter.cpp
@@ -84,7 +84,6 @@ int main(int argc, char* argv[]) {
   QApplication *_app = new QApplication(argc, argv);
 
   processArgumentsAndQueryMissing(argc, argv);
-  AbsDbAccess *_proddb = new BulkDbAccess();
 
   vector<string> apdSerials;
   vector<string> allApdSerials;
@@ -92,6 +91,12 @@ int main(int argc, char* argv[]) {
   allApdSerials = loadSerialsFromFileName(fileName);
   apdSerials.clear();
 
+  AbsDbAccess* _proddb;
+  if (apdSerials.size() > 30)
+    _proddb = new BulkDbAccess();
+  else
+    _proddb = new ProdDbAccess();
+
   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 ) )
diff --git a/getbatch.cpp b/getbatch.cpp
index ae1ae29..47a9d71 100644
--- a/getbatch.cpp
+++ b/getbatch.cpp
@@ -10,6 +10,7 @@
 
 // Custom
 #include "ProdDbAccess.h"
+#include "BulkDbAccess.h"
 #include "toolbox.cpp"
 
 using namespace std;
@@ -26,11 +27,11 @@ int main(int argc, char* argv[]) {
     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);
+  AbsDbAccess* _dba = suitedDbAccess(apdSerials.size());
 
   for (size_t i = 0 ; i < apdSerials.size() ; i++ ) {
     if (apdSerials[i].size() == 9) apdSerials[i] = "0" + apdSerials[i];
diff --git a/getirradiationstatus.cxx b/getirradiationstatus.cxx
index 97e0d5e..91cbad5 100644
--- a/getirradiationstatus.cxx
+++ b/getirradiationstatus.cxx
@@ -9,6 +9,7 @@
 #include <boost/program_options.hpp>
 
 // Custom
+#include "BulkDbAccess.h"
 #include "ProdDbAccess.h"
 #include "toolbox.cpp"
 
@@ -26,11 +27,11 @@ int main(int argc, char* argv[]) {
     cerr << "ERROR: Too many parameters! Please only provide the name of a text file containing the serials." << endl;
     exit(1);
   }
-  ProdDbAccess* _dba = new ProdDbAccess();
 
   fileName = string(argv[1]);
 
   vector<string> apdSerials = loadSerialsFromFileName(fileName);
+  AbsDbAccess* _dba = suitedDbAccess(apdSerials.size());
 
   for (size_t i = 0 ; i < apdSerials.size() ; i++ ) {
     if (apdSerials[i].size() == 9) apdSerials[i] = "0" + apdSerials[i];
diff --git a/getlocations.cpp b/getlocations.cpp
index ef7f5d2..9df41ca 100644
--- a/getlocations.cpp
+++ b/getlocations.cpp
@@ -9,6 +9,7 @@
 #include <boost/program_options.hpp>
 
 // Custom
+#include "BulkDbAccess.h"
 #include "ProdDbAccess.h"
 #include "toolbox.cpp"
 
@@ -26,15 +27,15 @@ int main(int argc, char* argv[]) {
     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);
+  AbsDbAccess* _dba = suitedDbAccess(apdSerials.size());
 
   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;
+    cout << "S/N " << apdSerials[i] << " is in Box " << _dba->box( uint( stoul( apdSerials[i] ) ) ) << " on position " << _dba->pos_in_box( uint( stoul( apdSerials[i] ) ) ) << ", Location: '" << _dba->location( uint( stoul( apdSerials[i] ) ) ) << "'" << endl;
   }
 
   return 0;
diff --git a/getu100.cxx b/getu100.cxx
index 8008cda..e769a15 100644
--- a/getu100.cxx
+++ b/getu100.cxx
@@ -10,6 +10,7 @@
 
 // Custom
 #include "ProdDbAccess.h"
+#include "BulkDbAccess.h"
 #include "toolbox.cpp"
 
 using namespace std;
@@ -26,11 +27,11 @@ int main(int argc, char* argv[]) {
     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);
+  AbsDbAccess* _dba = suitedDbAccess(apdSerials.size());
 
   for (size_t i = 0 ; i < apdSerials.size() ; i++ ) {
     if (apdSerials[i].size() == 9) apdSerials[i] = "0" + apdSerials[i];
diff --git a/gridListReader.cxx b/gridListReader.cxx
new file mode 100644
index 0000000..08872af
--- /dev/null
+++ b/gridListReader.cxx
@@ -0,0 +1,125 @@
+#include "gridListReader.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>
+
+gridListReader::gridListReader( int gridnumber , bool debug )
+  : networkManager ( new QNetworkAccessManager ( this ) ) {
+  _debug = debug;
+  gridApdListApiUrl = gridApdListBaseUrl + "?gridnumber=" + std::to_string(gridnumber);
+  getAndProcessData();
+}
+
+void gridListReader::getAndProcessData() {
+  try {
+    getApdListFromNetwork();
+    verifyHttpResponse();
+    getDataFromReply();
+    verifyDataFormat();
+    verifyQuerySuccess();
+    getApdNodeList();
+    readyToReadData = true;
+  }
+  catch ( std::exception& e ) {
+    std::cerr << e.what() << std::endl;
+    readyToReadData = false;
+  }
+}
+
+void gridListReader::getApdListFromNetwork() {
+  if ( _debug )
+    std::cout << "Initiating network request to " << gridApdListApiUrl << std::endl;
+
+  replyWithFullList = networkManager->get ( QNetworkRequest ( QUrl ( gridApdListApiUrl.c_str() ) ) );
+  connect ( replyWithFullList, SIGNAL ( finished() ), &_waitForNetwork, SLOT ( quit() ) );
+  _waitForNetwork.exec();
+
+  if ( _debug )
+    std::cout << "Done, received reply." << std::endl;
+}
+
+void gridListReader::verifyHttpResponse() {
+  if ( replyWithFullList->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 ( replyWithFullList->attribute ( QNetworkRequest::HttpStatusCodeAttribute ).toInt() );
+    throw std::runtime_error ( exceptionMsg.toUtf8().constData() );
+  }
+}
+void gridListReader::getDataFromReply() {
+  QString errorMsg;
+  int errorLine;
+  int errorColumn;
+
+  documentFromNetworkReply = new QDomDocument();
+  if ( _debug )
+    std::cout << "Setting content of network reply to QDomDocument, verifying response..." << std::endl;
+  if ( !documentFromNetworkReply->setContent ( replyWithFullList, &errorMsg, &errorLine, &errorColumn ) ) {
+    const QString exceptionMsg = QString::fromUtf8 ( "Error in parsing data" );
+    throw std::runtime_error ( exceptionMsg.toUtf8().constData() );
+  }
+}
+
+void gridListReader::verifyDataFormat() {
+  if ( documentFromNetworkReply->documentElement().nodeName() != QString::fromUtf8 ( "productiondb" )
+       || documentFromNetworkReply->documentElement().firstChild().nodeName() != QString::fromUtf8 ( "request" ) )
+    throw std::runtime_error ( "APD data has invalid format." );
+
+  QDomNode requestResult = documentFromNetworkReply->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." );
+}
+
+void gridListReader::verifyQuerySuccess() {
+  QDomNode requestResult = documentFromNetworkReply->documentElement().firstChild();
+  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() );
+  }
+}
+
+void gridListReader::getApdNodeList() {
+  nodeListApds = new QDomNodeList( documentFromNetworkReply->documentElement().elementsByTagName ( QString::fromUtf8 ( "apd" ) ) );
+  if ( _debug )
+    std::cout << "Got " << nodeListApds->length() << " APDs!" << std::endl;
+}
+
+std::vector<std::string> gridListReader::getListOfSerials() {
+  try {
+    return getSerialListIfDataCanBeRead();
+  }
+  catch ( std::exception& e ) {
+    std::cerr << e.what() << std::endl;
+    std::vector<std::string> serialList;
+    serialList.clear();
+    return serialList;
+  }
+}
+
+std::vector<std::string> gridListReader::getSerialListIfDataCanBeRead() {
+  if (!readyToReadData) throw std::runtime_error ( "No data loaded that can be read!" );
+  std::vector<std::string> serialList = getSerialListFromNodeList();
+  if ( _debug )
+    std::cout << "Returning serial list with " << serialList.size() << " entries!" << std::endl;
+  return serialList;
+}
+
+std::vector<std::string> gridListReader::getSerialListFromNodeList() {
+  std::vector<std::string> serialList;
+  serialList.clear();
+  for ( int 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);
+  }
+  return serialList;
+}
diff --git a/gridListReader.h b/gridListReader.h
new file mode 100644
index 0000000..a8a489b
--- /dev/null
+++ b/gridListReader.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <vector>
+#include <QtCore/QObject>
+#include <QtCore/QEventLoop>
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class QDomDocument;
+class QDomNodeList;
+
+class gridListReader : public QObject {
+
+private:
+  bool _debug = false;
+  bool readyToReadData = false;
+  const std::string gridApdListBaseUrl = "https://ep1.ruhr-uni-bochum.de/endcapProductionDB/api/grid_listapds.php";
+  std::string gridApdListApiUrl = "";
+
+  QNetworkAccessManager* networkManager;
+  QNetworkReply *replyWithFullList;
+  QDomDocument *documentFromNetworkReply;
+  QDomNodeList *nodeListApds;
+  QEventLoop _waitForNetwork;
+
+  void getApdListFromNetwork();
+  void verifyHttpResponse();
+  void getDataFromReply();
+  void verifyDataFormat();
+  void verifyQuerySuccess();
+  void getApdNodeList();
+
+  std::vector<std::string> getSerialListIfDataCanBeRead();
+  std::vector<std::string> getSerialListFromNodeList();
+
+public:
+  gridListReader(int gridnumber, bool debug = false);
+  void getAndProcessData();
+  std::vector<std::string> getListOfSerials();
+};
+
diff --git a/makeGridList.cxx b/makeGridList.cxx
new file mode 100644
index 0000000..1c8e295
--- /dev/null
+++ b/makeGridList.cxx
@@ -0,0 +1,83 @@
+#include "gridListReader.h"
+#include "QtCore/QCoreApplication"
+#include <vector>
+#include <string>
+#include <iostream>
+
+using namespace std;
+
+void processParameters(int argc, char *argv[]);
+int entryExistsInArgList(int argc, char *argv[], std::string parameterName);
+void getSerialListAsStringsFromDatabase();
+void convertStringListToNumbers();
+void sortNumericList();
+void outputApds();
+
+static bool debug = false;
+static vector<string> serialListString;
+static vector<unsigned long> serialList;
+static int gridnumber;
+
+int main(int argc, char *argv[]) {
+  processParameters(argc, argv);
+  QCoreApplication *_app = new QCoreApplication(argc,argv);
+
+  getSerialListAsStringsFromDatabase();
+  convertStringListToNumbers();
+  sortNumericList();
+  outputApds();
+
+  _app->exit(0);
+  return 0;
+}
+
+void processParameters(int argc, char *argv[]) {
+  if ( argc == 3 && entryExistsInArgList(argc, argv, "debug")) {
+    debug = true;
+  }
+  else if (argc != 2 ) {
+    cerr << "Too few or too many parameters!\n"
+         << "Pass first and last serial of range." << endl;
+    exit(1);
+  }
+
+  gridnumber = stoi(argv[1]);
+}
+
+int entryExistsInArgList(int argc, char *argv[], std::string parameterName) {
+  for (int i = 0; i < argc; i++) {
+    if (argv[i] == parameterName) return true;
+  }
+  return false;
+}
+
+void getSerialListAsStringsFromDatabase() {
+  gridListReader *_reader = new gridListReader(gridnumber, debug);
+  serialListString = _reader->getListOfSerials();
+}
+
+void convertStringListToNumbers() {
+  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;
+    exit(2);
+  }
+}
+
+void sortNumericList() {
+  std::sort(serialList.begin(), serialList.end());
+  std::cerr << "Sorted!" << std::endl;
+}
+
+void outputApds() {
+  int numOut = 0;
+  for ( size_t i = 0 ; i < serialList.size() ; i++) {
+    std::string thisSerial = std::to_string(serialList[i]);
+    while (thisSerial.length() < 10)
+      thisSerial = std::string("0") + thisSerial;
+    cout << thisSerial << endl;
+    numOut++;
+  }
+  cerr << "Found " << numOut << " APDs in the given range." << endl;
+}
diff --git a/testxmlstructure.cxx b/testxmlstructure.cxx
index 12348b1..6c9de84 100644
--- a/testxmlstructure.cxx
+++ b/testxmlstructure.cxx
@@ -3,31 +3,52 @@
 
 #include <QtCore/QString>
 #include <QtXml/QXmlStreamWriter>
+#include "productiondatabaseclient.h"
 
 int main() {
   std::vector<QString> apds;
-  apds.push_back(QString::fromUtf8("1234567890"));
-  apds.push_back(QString::fromUtf8("4567890123"));
-  apds.push_back(QString::fromUtf8("7890123456"));
-
-  int batch = 1;
+  apds.push_back(QString::fromUtf8("0608004648"));
+  apds.push_back(QString::fromUtf8("APD123"));
+  apds.push_back(QString::fromUtf8("APD456"));
 
   QString buffer;
   QXmlStreamWriter xml ( &buffer );
   xml.setAutoFormatting ( true );
   xml.writeStartDocument ( QString::fromUtf8 ( "1.0" ) );
   xml.writeStartElement ( QString::fromUtf8 ( "productiondb" ) );
-  xml.writeTextElement ( QString::fromUtf8 ( "batch_type" ), QString::fromUtf8 ( "after irradiation" ) );
   xml.writeStartElement ( QString::fromUtf8 ( "elements" ) );
   int j = 1;
   for ( auto i = apds.begin(); i != apds.end(); i++, j++ ) {
     xml.writeStartElement ( QString::fromUtf8 ( "apd" ) );
     xml.writeTextElement ( QString::fromUtf8 ( "serial" ), *i );
-    xml.writeTextElement ( QString::fromUtf8 ( "batch_number" ), QString::number ( batch, 10 ) );
-    xml.writeTextElement ( QString::fromUtf8 ( "position" ), QString::number( j, 10 ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m100_warm" ), QString::number( 100.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m100_cold" ), QString::number( 100.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m100_warm" ), QString::number( 10.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m100_cold" ), QString::number( 10.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m150_warm" ), QString::number( 150.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m150_cold" ), QString::number( 150.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m150_warm" ), QString::number( 15.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m150_cold" ), QString::number( 15.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m200_warm" ), QString::number( 200.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m200_cold" ), QString::number( 200.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m200_warm" ), QString::number( 20.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m200_cold" ), QString::number( 20.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m300_warm" ), QString::number( 300.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "u_m300_cold" ), QString::number( 300.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m300_warm" ), QString::number( 30.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "s_m300_cold" ), QString::number( 30.4 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "ubreak_warm" ), QString::number( 30.3 + j ) );
+    xml.writeTextElement ( QString::fromUtf8 ( "ubreak_cold" ), QString::number( 30.4 + j ) );
     xml.writeEndElement();
   }
   xml.writeEndDocument(); // This closes all open tags automatically.
 
-  std::cerr << buffer.toStdString() << std::endl;
+  std::string xmlRequest = buffer.toStdString();
+  std::cerr << xmlRequest << std::endl;
+
+//  ProductionDatabase::ProductionDatabaseClient *proddbclient = new ProductionDatabase::ProductionDatabaseClient();
+//  std::cerr << proddbclient->getVersionShort() << std::endl;
+//  proddbclient->queryCredentials();
+//  proddbclient->checkConnectivityAndCredentials();
+//  proddbclient->postXmlRequest("apd_enter_characteristics.php", xmlRequest);
 }
diff --git a/toolbox.cpp b/toolbox.cpp
index 6d3f5ad..88fd14d 100644
--- a/toolbox.cpp
+++ b/toolbox.cpp
@@ -3,6 +3,9 @@
 #include <string>
 #include <vector>
 
+#include "ProdDbAccess.h"
+#include "BulkDbAccess.h"
+
 using namespace std;
 
 vector<string> loadSerialsFromFileName(string m_fileName, bool m_debug = false) {
@@ -26,4 +29,17 @@ vector<string> loadSerialsFromFileName(string m_fileName, bool m_debug = false)
     return result;
 }
 
-
+AbsDbAccess *suitedDbAccess(size_t nAPDs, bool debug = false) {
+  AbsDbAccess *result;
+  if (nAPDs > 30) {
+    if (debug)
+      std::cout << "Initializing BulkDbAccess which becomes efficient for many queries. Patience, this might take a minute...";
+    result = new BulkDbAccess();
+  }
+  else {
+    if (debug)
+      std::cout << "Initializing BulkDbAccess which becomes efficient for many queries. Patience, this might take a minute...";
+    result = new ProdDbAccess();
+  }
+  return result;
+}
-- 
GitLab