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;
+}
+
+