diff --git a/.gitignore b/.gitignore
index b41bd80b7d96c86c140f215c7520124558055e0a..3922bd31fda3fc2193789174eb9456bfeac0ae1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 # This file is used to ignore files which are generated
 # ----------------------------------------------------------------------------
 
+CMakeFiles/
+
 *~
 *.autosave
 *.a
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml.tempoff
similarity index 100%
rename from .gitlab-ci.yml
rename to .gitlab-ci.yml.tempoff
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd1151da58aca3fec27f203a54bca64b71e460b3..ae7120fb9535b89c78cb098f3842c723c1a9cf65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,10 +7,11 @@ find_package(Qt5 COMPONENTS Core Gui Widgets Network Xml)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/includes/)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/proddb/)
 INCLUDE_DIRECTORIES($ENV{proddb_clientlib_includes})
+INCLUDE_DIRECTORIES("/home/tau/jreher/git/proddb-clientlib")
 LINK_DIRECTORIES($ENV{proddb_clientlib_libpath})
 
-add_library(toolbox "toolbox.cpp" "serialListReader.h" "serialListReader.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_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" "proddb/AbsDbAccess.h")
 
 add_executable(apdBatchSetter "apdbatchsetter.cpp")
 add_executable(apdLocationSetter "apdlocationsetter.cpp")
@@ -19,29 +20,36 @@ add_executable(setSentForAnalysisAfterIrradiation "apdSetSentForAnalysisAfterIrr
 add_executable(setIrradiationInfo "apdSetIrradiationInfo.cpp")
 add_executable(setAnnealingInfo "apdSetAnnealingInfo.cpp")
 add_executable(apdBoxSetter "apdboxsetter.cpp")
+add_executable(apdUnavailableSetter "apdunavailablesetter.cpp")
 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(apdUnitCreator "apdUnitCreator.cpp")
+add_executable(validateSerials "validateSerials.cpp")
 
 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(apdUnavailableSetter proddbaccess proddbclient boost_program_options)
+target_link_libraries(apdUnitCreator 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)
+target_link_libraries(validateSerials proddbaccess)
diff --git a/apdSetAnnealingInfo.cpp b/apdSetAnnealingInfo.cpp
index 52a9987b0d0d331670ac7ce09ec8d05631e12bf2..a7e54e18a48c446068ec5c7506e999172c289114 100644
--- a/apdSetAnnealingInfo.cpp
+++ b/apdSetAnnealingInfo.cpp
@@ -9,7 +9,7 @@
 #include "toolbox.cpp"
 #include "BulkDbAccess.h"
 
-#include "QtGui/QApplication"
+#include "QApplication"
 
 namespace po=boost::program_options;
 using namespace std;
diff --git a/apdSetIrradiationInfo.cpp b/apdSetIrradiationInfo.cpp
index ddc40a1a837969d84ab5d3f428b3200f616ae6f9..b206c91d875112fa4c5b827c19029d75de940c69 100644
--- a/apdSetIrradiationInfo.cpp
+++ b/apdSetIrradiationInfo.cpp
@@ -9,7 +9,7 @@
 #include "toolbox.cpp"
 #include "BulkDbAccess.h"
 
-#include "QtGui/QApplication"
+#include "QApplication"
 
 namespace po=boost::program_options;
 using namespace std;
diff --git a/apdSetSentForAnalysisAfterIrradiation.cpp b/apdSetSentForAnalysisAfterIrradiation.cpp
index 89d1ac86baec58bc3c525167faad2ad8090d7053..8d139da817af201da51c850130b262a97c0486dc 100644
--- a/apdSetSentForAnalysisAfterIrradiation.cpp
+++ b/apdSetSentForAnalysisAfterIrradiation.cpp
@@ -9,7 +9,7 @@
 #include "toolbox.cpp"
 #include "BulkDbAccess.h"
 
-#include "QtGui/QApplication"
+#include "QApplication"
 
 namespace po=boost::program_options;
 using namespace std;
diff --git a/apdUnitCreator.cpp b/apdUnitCreator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10095d9df34e5eb33f2e1bebb5fedbb371ff0076
--- /dev/null
+++ b/apdUnitCreator.cpp
@@ -0,0 +1,194 @@
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <vector>
+
+#include <productiondatabaseclient.h>
+#include <boost/program_options.hpp>
+
+using namespace std;
+using namespace ProductionDatabase;
+namespace po = boost::program_options;
+
+struct unitInfo {
+    std::string redSerial = "";
+    std::string blueSerial = "";
+    std::string crystalSerial = "";
+    uint barCode = 0;
+};
+
+static string fileName = "serials.dat";
+static int boxNo = 0;
+static bool debug = false;
+static string username = "";
+static string password = "";
+
+std::string moduleSN;
+static bool makeCapsules = true;
+static bool makeUnits = true;
+static bool assignToModule = false;
+
+static vector<unitInfo> newUnits;
+
+void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]);
+void loadSerialsFromFileName(string m_fileName, bool m_debug = false);
+
+int main(int argc, char* argv[]) {
+    processArgumentsAndQueryMissing(argc, argv);
+    ProductionDatabaseClient *proddb = new ProductionDatabaseClient();
+
+    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;
+
+    loadSerialsFromFileName(fileName);
+
+    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;
+        return response;
+    }
+    if (debug) cout << "Connection successful!" << endl;
+
+    int nFailed = 0;
+
+    std::vector<uint> barcodes;
+    if (assignToModule && newUnits.size() == 8)
+        for (size_t i = 0; i < 8; i++)
+            barcodes.push_back(0);
+
+    for (auto newUnit = newUnits.begin(); newUnit < newUnits.end(); newUnit++) {
+        try {
+            if (makeCapsules) {
+                proddb->createApdCapsule(newUnit->redSerial, newUnit->blueSerial);
+            }
+            if (makeUnits) {
+                std::string capsuleSerial = newUnit->blueSerial + "/" + newUnit->redSerial;
+                proddb->createApdUnit(capsuleSerial, newUnit->crystalSerial, std::to_string(newUnit->barCode));
+            }
+            if (assignToModule) {
+                barcodes.push_back(newUnit->barCode);
+            }
+        }
+        catch (std::exception &e) {
+            nFailed++;
+            std::cerr << "An error occurred on APD paid with red APD " << newUnit->redSerial << " and blue APD " << newUnit->blueSerial << "!" << std::endl;
+            std::cerr << e.what() << std::endl << std::endl;
+            continue;
+        }
+    }
+
+    if (assignToModule) {
+        if (barcodes.size() != 16)
+            cerr << "Assignments are only possible if there are 8 or 16 Units in file!" << std::endl;
+        else {
+            try {
+                proddb->assignUnitToModule(moduleSN, barcodes);
+            }
+            catch (std::exception &e) {
+                std::cerr << "An error occurred while assigning APDs to Module " << moduleSN << std::endl;
+                std::cerr << e.what() << std::endl << std::endl;
+            }
+        }
+    }
+
+    cout << "Created " << newUnits.size() << " APD Capsules and assigned them to Units." << std::endl;
+    if (nFailed > 0)
+        cerr << "Data entry failed for " << nFailed << " entries!" << std::endl;
+
+    return (-newUnits.empty());
+}
+
+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 or serial numbers [serials.dat]")
+            ("noMakeCapsules", "Skip the step of creating capsules from APDs")
+            ("noMakeUnits", "Skip the step of creating units from capsules")
+            ("assignToModule", po::value<string>(), "Skip the step of assigning units to submodules")
+            ("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);
+    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;
+    }
+    else {
+        throw "Please enter a file name!";
+    }
+    if (vm.count("noMakeCapsules")) {
+        cout << "Skipping capsule creation. Only works if capsules already exist!" << endl;
+        makeCapsules = false;
+    }
+    if (vm.count("noMakeUnits")) {
+        cout << "Skipping unit creation. Module assignment only works if units already exist!" << endl;
+        makeUnits= false;
+    }
+    if (vm.count("assignToModule")) {
+        moduleSN = vm["assignToModule"].as<string>();
+        cout << "Will assign units to submodule " << moduleSN << std::endl;
+        assignToModule = 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;
+    }
+}
+
+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);
+    }
+
+    newUnits.clear();
+
+    int pos = 1;
+
+    string line = "";
+    while (in.good()) {
+        getline(in,line);
+        if (line.length() == 0 || !isdigit(line[0])) {
+          pos++;
+          continue;
+        }
+        unitInfo newUnit;
+
+        uint barcode;
+
+        std::stringstream linestream(line);
+        linestream >> newUnit.redSerial >> newUnit.blueSerial >> newUnit.crystalSerial >> barcode;
+
+        if (barcode == 0) continue;
+        if (barcode <1000000 )
+        barcode += 1309000000;
+        newUnit.barCode = barcode;
+
+        std::cout << "Found new unit with RS = " << newUnit.redSerial << ", BS = " << newUnit.blueSerial << ", CS = " << newUnit.crystalSerial << ", BC = " << newUnit.barCode << std::endl;
+        
+        newUnits.push_back(newUnit);
+    }
+
+    cerr << "Found " << newUnits.size() << " new Units to be entered." << endl;
+    return;
+}
+
+
diff --git a/apdbatchsetter.cpp b/apdbatchsetter.cpp
index 8b53083d1c6d5c13b20cdac706b1b6a030e8736f..898cf13974333c4733430f397735fbbc3c2eb008 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 5125d0d10a1c64e8d2d451e8069ad57b1efab4ee..457447014b7ba530f97fa13aa8d86cef5ca3616b 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 8c29d368e6ad562665fca9dd58583bc0a09ccc53..03cb46770fbd89408b32f5e7dcf9c570677e4238 100644
--- a/apdlocationsetter.cpp
+++ b/apdlocationsetter.cpp
@@ -3,13 +3,14 @@
 #include <iomanip>
 #include <string>
 #include <vector>
+#include <regex>
 
 #include <productiondatabaseclient.h>
 #include <boost/program_options.hpp>
 #include "toolbox.cpp"
 #include "BulkDbAccess.h"
 
-#include "QtGui/QApplication"
+#include "QApplication"
 
 namespace po=boost::program_options;
 using namespace std;
@@ -84,7 +85,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,10 +92,16 @@ 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 ) )
-          apdSerials.push_back(allApdSerials[i]);
+          apdSerials.push_back(std::regex_replace(allApdSerials[i],std::regex(R"([\D])"), ""));
   }
 
   if ( apdSerials.size() == 0 ) {
diff --git a/apdsetarrivalforirradiation.cpp b/apdsetarrivalforirradiation.cpp
index 26ca5fc9222ec39f50bf6aee1a1ce71c0c243f6f..e7a416ff84279a6884e95ff6fe475df36f8adafc 100644
--- a/apdsetarrivalforirradiation.cpp
+++ b/apdsetarrivalforirradiation.cpp
@@ -9,7 +9,7 @@
 #include "toolbox.cpp"
 #include "BulkDbAccess.h"
 
-#include "QtGui/QApplication"
+#include "QApplication"
 
 namespace po=boost::program_options;
 using namespace std;
diff --git a/apdunavailablesetter.cpp b/apdunavailablesetter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..afd95bdb98bbbd430d8811d12c3c591abd7d704f
--- /dev/null
+++ b/apdunavailablesetter.cpp
@@ -0,0 +1,110 @@
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <vector>
+
+#include <productiondatabaseclient.h>
+#include <boost/program_options.hpp>
+
+using namespace std;
+using namespace ProductionDatabase;
+namespace po = boost::program_options;
+
+static string fileName = "serials.dat";
+static bool unavailable = true;
+static bool debug = false;
+static string username = "";
+static string password = "";
+
+static vector<string> apdSerials;
+
+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 apdUnavailableSetter with version " << proddb->getVersion() << " of the database access libraries." << endl << endl
+                    << "Now trying to set tempUnavailable to " << unavailable << " for ADPs from serial file " << fileName << endl;
+
+    loadSerialsFromFileName(fileName);
+
+    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;
+        return response;
+    }
+    if (debug) cout << "Connection successful!" << endl;
+
+    proddb->setApdTempUnavailable(apdSerials,unavailable);
+
+    cout << "\nTemporary unavailability" << unavailable << " 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 apdUnavailableSetter! :)" << endl << endl;
+
+    return (-apdSerials.empty());
+}
+
+void processArgumentsAndQueryMissing(int m_argc, char* m_argv[]) {
+    po::options_description desc("Available options");
+    desc.add_options()
+            ("help", "produce help message")
+            ("available", "Set temporary unavailability to false instead of true")
+            ("fileName", po::value<string>(), "file name or serial numbers [serials.dat]")
+            ("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);
+    po::notify(vm);
+
+    if (vm.count("help")) {
+        cout << desc << "\n";
+        exit(0);
+    }
+    if (vm.count("available"))
+        unavailable = false;
+
+    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("debug")) {
+        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();
+
+    string line = "";
+    while (in.good()) {
+        getline(in,line);
+        if (line.length() == 0 || !isdigit(line[0]))
+          continue;
+        while (line.length() != 10)
+          line = std::string("0")+ line;
+        apdSerials.push_back(line);
+        if (m_debug) cout << "Found serial: " << line << endl;
+    }
+    cerr << "Found " << apdSerials.size() << " APDs" << endl;
+    return;
+}
+
+
diff --git a/getbatch.cpp b/getbatch.cpp
index ae1ae293eb2a2b85d849ceaf71806a7e877b64f9..ab8c3aa1805014994c186f78e0e9faf92c743fb8 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,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] << " New: " << _dba->getBatchNew( uint( stoul( apdSerials[i] ) ) ) << " Old: " << _dba->getBatchIrr( uint( stoul( apdSerials[i] ) ) ) << endl;
+    cout << "S/N: " << apdSerials[i] << " New: " << _dba->getBatchNew( uint( stoul( apdSerials[i] ) ) ) << " Irradiated: " << _dba->getBatchIrr( uint( stoul( apdSerials[i] ) ) ) << endl;
   }
 
   return 0;
diff --git a/getirradiationstatus.cxx b/getirradiationstatus.cxx
index 97e0d5e77b5aee1698ffb6f32a6c4dcfdd7586a6..91cbad5a8ba8387ad84425e9a451b80586466653 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 ef7f5d2731942d428876299931603fa645b41a03..61649b1a79170c29e1d339f6d6085af4dacf05af 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,18 @@ 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;
+    if (_dba->apdAvailable( uint( stoul ( apdSerials[i] ) ) ) )
+      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;
+    else
+      cout << "UNAVAILABLE S/N " << apdSerials[i] << " was assigned to 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 8008cda2d5586be502cc0af50ff71ed6d5817e90..e769a151d94a1bdf3937626d0a40399be4845fc3 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 0000000000000000000000000000000000000000..08872afef09b2adc9f0e3b23e75aceb4d00e863f
--- /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 0000000000000000000000000000000000000000..a8a489bfe298c9edbfbe484f9d6f2e7d08911668
--- /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 0000000000000000000000000000000000000000..1c8e2952e5c09a530b44534e1cfff3f367368ef9
--- /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/makeseriallist.cxx b/makeseriallist.cxx
index fe663669dbbe64d629080c24dcf6ecb0de21a7aa..f0e6f230e19ba285ed92c75aaf509f2ef3af0e1f 100644
--- a/makeseriallist.cxx
+++ b/makeseriallist.cxx
@@ -13,6 +13,7 @@ void convertStringListToNumbers();
 void outputApdsInRange();
 
 static bool debug = false;
+static bool backwards = false;
 static vector<string> serialListString;
 static vector<unsigned long> serialList;
 static unsigned long serialMin;
@@ -31,17 +32,31 @@ int main(int argc, char *argv[]) {
 }
 
 void processParameters(int argc, char *argv[]) {
-  if ( argc == 4 && entryExistsInArgList(argc, argv, "debug")) {
+  size_t validargs = 0;
+  if (entryExistsInArgList(argc, argv, "debug")) {
     debug = true;
+    validargs++;
   }
-  else if (argc != 3 ) {
-    cerr << "Too few or too many parameters!\n"
-         << "Pass first and last serial of range." << endl;
-    exit(1);
+  if (entryExistsInArgList(argc, argv, "backwards")) {
+    backwards = true;
+    validargs++;
+  }
+  if (argc != validargs+3 ) {
+      cerr << "Too few or too many parameters!\n"
+          << "Pass first and last serial of range." << endl;
+      exit(1);
   }
 
   serialMin = stoul(argv[1]);
   serialMax = stoul(argv[2]);
+
+  if (serialMin > serialMax) {
+      unsigned long oldMin = serialMin;
+      serialMin = serialMax;
+      serialMax = oldMin;
+      cerr << "Warning; Minimum is greater than Maximum, switching them and turning on backwards mode!" << endl;
+      backwards = true;
+  }
 }
 
 int entryExistsInArgList(int argc, char *argv[], std::string parameterName) {
@@ -68,8 +83,11 @@ void convertStringListToNumbers() {
 void outputApdsInRange() {
   int numOut = 0;
   for ( size_t i = 0 ; i < serialList.size() ; i++) {
-    if ( serialList [i] >= serialMin && serialList [i] <= serialMax ) {
-      cout << serialListString[i] << endl;
+    size_t j = i;
+    if (backwards) 
+      j = serialList.size() - (i+1);
+    if ( serialList [j] >= serialMin && serialList [j] <= serialMax ) {
+      cout << serialListString[j] << endl;
       numOut++;
     }
   }
diff --git a/makeseriallist2.cxx b/makeseriallist2.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fe663669dbbe64d629080c24dcf6ecb0de21a7aa
--- /dev/null
+++ b/makeseriallist2.cxx
@@ -0,0 +1,77 @@
+#include "serialListReader.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 outputApdsInRange();
+
+static bool debug = false;
+static vector<string> serialListString;
+static vector<unsigned long> serialList;
+static unsigned long serialMin;
+static unsigned long serialMax;
+
+int main(int argc, char *argv[]) {
+  processParameters(argc, argv);
+  QCoreApplication *_app = new QCoreApplication(argc,argv);
+
+  getSerialListAsStringsFromDatabase();
+  convertStringListToNumbers();
+  outputApdsInRange();
+
+  _app->exit(0);
+  return 0;
+}
+
+void processParameters(int argc, char *argv[]) {
+  if ( argc == 4 && entryExistsInArgList(argc, argv, "debug")) {
+    debug = true;
+  }
+  else if (argc != 3 ) {
+    cerr << "Too few or too many parameters!\n"
+         << "Pass first and last serial of range." << endl;
+    exit(1);
+  }
+
+  serialMin = stoul(argv[1]);
+  serialMax = stoul(argv[2]);
+}
+
+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() {
+  serialListReader *_reader = new serialListReader(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 outputApdsInRange() {
+  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;
+}
diff --git a/proddb/AbsDbAccess.h b/proddb/AbsDbAccess.h
new file mode 120000
index 0000000000000000000000000000000000000000..38f3f608d2adf8f7a1128a2cabeb36e9c0590fbf
--- /dev/null
+++ b/proddb/AbsDbAccess.h
@@ -0,0 +1 @@
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/AbsDbAccess.h
\ No newline at end of file
diff --git a/proddb/BulkDbAccess.cxx b/proddb/BulkDbAccess.cxx
index ac6f43dbf53ab6b2bb8466edea984efccf8a6f4d..ce93c985e06a08fc6d21064a3c23a87129ecc294 120000
--- a/proddb/BulkDbAccess.cxx
+++ b/proddb/BulkDbAccess.cxx
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/BulkDbAccess.cxx
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/BulkDbAccess.cxx
\ No newline at end of file
diff --git a/proddb/BulkDbAccess.h b/proddb/BulkDbAccess.h
index ac1ded2248a5bf838f616c41d0f741b16c696748..c1a16e4d57f84b75ecf609c8fe8127f84fdef7b5 120000
--- a/proddb/BulkDbAccess.h
+++ b/proddb/BulkDbAccess.h
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/BulkDbAccess.h
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/BulkDbAccess.h
\ No newline at end of file
diff --git a/proddb/ProdDbAccess.cxx b/proddb/ProdDbAccess.cxx
index 4f9fae5b7277d1ee5950b7ab8115494dd0047f1e..28059475347d84d826fcd12603c3b85b628bf5ff 120000
--- a/proddb/ProdDbAccess.cxx
+++ b/proddb/ProdDbAccess.cxx
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProdDbAccess.cxx
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProdDbAccess.cxx
\ No newline at end of file
diff --git a/proddb/ProdDbAccess.h b/proddb/ProdDbAccess.h
index 94b012d69563fc321d3d3b9bdf30bb5d8af6a62d..4046fec49072cb8403820dea286842485110ea31 120000
--- a/proddb/ProdDbAccess.h
+++ b/proddb/ProdDbAccess.h
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProdDbAccess.h
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProdDbAccess.h
\ No newline at end of file
diff --git a/proddb/ProductionDatabase.cxx b/proddb/ProductionDatabase.cxx
index 614b2632e5ea940531fdd99a67da48c4901ea870..a9a096ce58238f709ba56c0281949cebc5bd4e88 120000
--- a/proddb/ProductionDatabase.cxx
+++ b/proddb/ProductionDatabase.cxx
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProductionDatabase.cxx
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProductionDatabase.cxx
\ No newline at end of file
diff --git a/proddb/ProductionDatabase.h b/proddb/ProductionDatabase.h
index 0aefe0e4778d1418c7060d24eebf6ba83745f502..1479557432ee5b993a203bcc634087c1477e626d 120000
--- a/proddb/ProductionDatabase.h
+++ b/proddb/ProductionDatabase.h
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProductionDatabase.h
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProductionDatabase.h
\ No newline at end of file
diff --git a/proddb/ProductionDatabaseApdStorage.cxx b/proddb/ProductionDatabaseApdStorage.cxx
index e4453cc02d9f689746fb21aa8c6f1a469a73276d..94a251d7093a95dcfb2ba90b92876cbe0971ea36 120000
--- a/proddb/ProductionDatabaseApdStorage.cxx
+++ b/proddb/ProductionDatabaseApdStorage.cxx
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProductionDatabaseApdStorage.cxx
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProductionDatabaseApdStorage.cxx
\ No newline at end of file
diff --git a/proddb/ProductionDatabaseApdStorage.h b/proddb/ProductionDatabaseApdStorage.h
index bbb81832b71512389bce5b47e6fdbfea038c3519..071a5c91491c49bb2da6073239bb46da705d7351 120000
--- a/proddb/ProductionDatabaseApdStorage.h
+++ b/proddb/ProductionDatabaseApdStorage.h
@@ -1 +1 @@
-/home/tau/jreher/git/ProtoSoft/DAQ/ApdCurves/ProductionDatabaseApdStorage.h
\ No newline at end of file
+/home/tau/jreher/git/ProtoSoft/DAQ/ProductionDatabase/ProductionDatabaseApdStorage.h
\ No newline at end of file
diff --git a/testxmlstructure.cxx b/testxmlstructure.cxx
index 12348b12ad4d3795634c9473a31b4b2a53e41fdc..98ed6211658f3dc7a16085a7754bae3e54ef10b0 100644
--- a/testxmlstructure.cxx
+++ b/testxmlstructure.cxx
@@ -2,32 +2,155 @@
 #include <iostream>
 
 #include <QtCore/QString>
-#include <QtXml/QXmlStreamWriter>
+#include <QtXml> ///QXmlStreamWriter> // For Compilation: QXml/QXmlStreamWriter
+#include "productiondatabaseclient.h"
+
+ProductionDatabase::ProductionDatabaseClient *proddbclient;
+
+void initialize();
+void screeningInfo();
+void assignApds(std::string redSerial, std::string blueSerial);
+void assignUnit(std::string detectorSerial, std::string crystalSerial, std::string barcode);
+void assignToModule(std::vector<uint> barcodes, std::string moduleSN);
 
 int main() {
-  std::vector<QString> apds;
-  apds.push_back(QString::fromUtf8("1234567890"));
-  apds.push_back(QString::fromUtf8("4567890123"));
-  apds.push_back(QString::fromUtf8("7890123456"));
+  initialize();
+  std::vector<uint> barcodes;
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(0);
+  barcodes.push_back(1309009619);
+  barcodes.push_back(1309009602);
+  barcodes.push_back(1309009633);
+  barcodes.push_back(1309009664);
+  barcodes.push_back(1309009657);
+  barcodes.push_back(1309009696);
+  barcodes.push_back(1309009640);
+  barcodes.push_back(1309009626);
+  assignToModule(barcodes, "29012015-9");
+  return 0;
+}
+
+void initialize() {
+  proddbclient = new ProductionDatabase::ProductionDatabaseClient();
+  std::cerr << proddbclient->getVersionShort() << std::endl;
+  proddbclient->queryCredentials();
+  proddbclient->checkConnectivityAndCredentials();
+}
+
+void assignToModule(std::vector<uint> barcodes, std::string moduleSN) {
+  if (barcodes.size() != 16) {
+    throw "Vector with barcodes must contain exactly 16 elements!";
+  }
+
+  QString buffer;
+  QXmlStreamWriter xml ( &buffer );
+
+  xml.setAutoFormatting(true);
+  xml.writeStartDocument( QString::fromUtf8( "1.0" ) );
+  xml.writeStartElement( QString::fromUtf8( "productiondb" ) ) ;
+  xml.writeTextElement("moduleSN",QString::fromStdString(moduleSN));
+
+  for (size_t i = 0; i < 16; i++) {
+    std::string entryname = std::string("Slot") + std::to_string(i);
+    if (barcodes[i]==0)
+        xml.writeEmptyElement(QString::fromStdString(entryname));
+    else
+        xml.writeTextElement(QString::fromStdString(entryname),QString::number(barcodes[i]));
+  }
+
+  xml.writeEndDocument();
+
+  std::string xmlRequest = buffer.toStdString();
+  std::cerr << xmlRequest << std::endl;
+
+  //proddbclient->postXmlRequest("",xmlRequest);
+}
+
+void assignUnit(std::string detectorSerial, std::string crystalSerial, std::string barcode) {
+  QString buffer;
+  QXmlStreamWriter xml ( &buffer );
+
+  xml.setAutoFormatting(true);
+  xml.writeStartDocument( QString::fromUtf8( "1.0" ) );
+  xml.writeStartElement( QString::fromUtf8( "productiondb" ) ) ;
+  xml.writeTextElement( QString::fromUtf8( "detectorSerial" ), QString::fromStdString( detectorSerial ) );
+  xml.writeTextElement( QString::fromUtf8( "crystalSerial" ), QString::fromStdString( crystalSerial ) );
+  xml.writeTextElement( QString::fromUtf8( "barcode" ), QString::fromStdString( barcode ) );
+  xml.writeEndDocument();
 
-  int batch = 1;
+  std::string xmlRequest = buffer.toStdString();
+  std::cerr << xmlRequest << std::endl;
+
+  proddbclient->postXmlRequest("unit_assemble.php", xmlRequest);
+}
+
+void assignApds(std::string redSerial, std::string blueSerial) {
+  QString buffer;
+  QXmlStreamWriter xml ( &buffer );
+
+  xml.setAutoFormatting(true);
+  xml.writeStartDocument( QString::fromUtf8( "1.0" ) );
+  xml.writeStartElement( QString::fromUtf8( "productiondb" ) ) ;
+  xml.writeTextElement( QString::fromUtf8( "redSerial" ), QString::fromStdString( redSerial ) );
+  xml.writeTextElement( QString::fromUtf8( "blueSerial" ), QString::fromStdString( blueSerial ) );
+  xml.writeEndDocument();
+
+  std::string xmlRequest = buffer.toStdString();
+  std::cerr << xmlRequest << std::endl;
+
+  proddbclient->postXmlRequest("detector_assemble.php", xmlRequest);
+}
+
+void screeningInfo() {
+  std::vector<QString> apds;
+  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 6d3f5ad3f952a0c9758c2b9105b10c00749c42da..88fd14da7dfbb8974a88314f27141745d0b758b0 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;
+}
diff --git a/validateSerials.cpp b/validateSerials.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dde47ad70e1ef87c34c12d5e004d1635781d2a11
--- /dev/null
+++ b/validateSerials.cpp
@@ -0,0 +1,81 @@
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <string>
+#include <fstream>
+#include <QCoreApplication>
+
+#include "ProdDbAccess.h"
+#include "BulkDbAccess.h"
+#include "toolbox.cpp"
+
+using namespace std;
+
+bool checkDuplicates(vector<string> serials);
+bool validateWithDB(vector<string> serials);
+
+int main(int argc, char* argv[]) {
+  if ( argc > 2 ) {
+    cerr << "ERROR: Too many parameters! Please only provide the name of a text file containing the serials needed." << endl;
+    exit(1);
+  }
+  std::string fileName = "";
+
+  if ( argc == 2)
+    fileName = string(argv[1]);
+  else
+    fileName = "serials.dat";
+
+  vector<string> apdSerials = loadSerialsFromFileName(fileName);
+  
+  if (apdSerials.size() != 30) {
+    if (apdSerials.size() < 30)
+      cerr << "There are less than 30 APDs in this file. This might indicate an error. Do you want to continue?" << std::endl;
+    else
+      cerr << "There are more than 30 APDs in this file. There can only be up to 30 APDs in one batch. Do you want to continue?" << std::endl;
+    std::string answer;
+    cin >> answer;
+    if (answer != "yes" && answer != "y")
+      return 1;
+  }
+
+  if (!checkDuplicates(apdSerials)) {
+    cerr << "At least one APD was found at least twice. Since an APD can't be in two slots at once, this serial file is invalid and will not be checked against the database." << std::endl;
+    return 1;
+  }
+
+  if (!validateWithDB(apdSerials)) {
+    cerr << "At least one APD was naughty!" << endl;
+    return 1;
+  }
+  cerr << "Santa will visit all of these APDs." << endl;
+  return 0;
+}
+
+bool checkDuplicates(vector<string> serials) {
+  bool result = true;
+  for (auto outerIterator = serials.begin(); outerIterator != serials.end(); outerIterator++) {
+    for (auto innerIterator = outerIterator; innerIterator != serials.end(); innerIterator++) {
+      if (innerIterator != outerIterator && *innerIterator == *outerIterator) {
+        result = false;
+        cerr << "Serial " << *innerIterator << " appears at least twice!" << std::endl;
+      }
+    }
+  }
+  return result;
+}
+
+bool validateWithDB(vector<string> serials) {
+  AbsDbAccess* _dba = suitedDbAccess(serials.size());
+  bool result = true;
+  for (auto iterator = serials.begin(); iterator != serials.end(); iterator++) {
+    if (_dba->apdAvailable(stoul(*iterator))) {
+      cout << "Serial " << *iterator << " is good!" << endl;
+    }
+    else {
+      cerr << "Serial " << *iterator << " was not found or is not available! ERROR!" << endl;
+      result = false;
+    }
+  }
+  return result;
+}