Compare commits

...

2 Commits

Author SHA1 Message Date
MassiveAtoms
e3451369e6 Documentation added 2019-07-02 15:40:37 -03:00
MassiveAtoms
99e509aa03 Documentation underway 2019-07-02 14:53:45 -03:00
10 changed files with 223 additions and 80 deletions

View File

@ -1,5 +1,13 @@
#include "headers/Park_time.h" #include "headers/Park_time.h"
/*
initializes everything, id is auto incremented from what's stored in the db.
inmediately saves to db upon creation.
Also, this weird syntax is called an initializer list, and is the preffered
method of how to initialize members. It has a measurable performance increase
because it uses move semantics instead of copy semantics.
https://www.geeksforgeeks.org/when-do-we-use-initializer-list-in-c/
*/
Park_time::Park_time(int c_id, int s_id) Park_time::Park_time(int c_id, int s_id)
: customer_id{c_id}, : customer_id{c_id},
spot_id{s_id}, spot_id{s_id},
@ -8,7 +16,9 @@ Park_time::Park_time(int c_id, int s_id)
id{auto_increment_db() + 1} { id{auto_increment_db() + 1} {
save_db(); save_db();
} }
/*
this one initializes with data from the database. should probably only be used in the query functions.
*/
Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_, Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_,
int duration_) int duration_)
: id{id_}, : id{id_},
@ -19,7 +29,12 @@ Park_time::Park_time(int id_, int customer_id_, int spot_id_, int start_,
end = time_point<system_clock>(seconds(start_ + duration_)); end = time_point<system_clock>(seconds(start_ + duration_));
} }
/*
simple checking if customer is clocking out at the right spot.
sets end(time of clocking out) and calculates the duration.
updates the info in the database.
*/
void Park_time::clock_out(int c_id, int s_id) { void Park_time::clock_out(int c_id, int s_id) {
if (c_id != customer_id) { if (c_id != customer_id) {
@ -52,7 +67,8 @@ std::ostream& operator<<(std::ostream& os, const Park_time& pt) {
os << "- - - - - - - - - - - - - - - - - - - -\n"; os << "- - - - - - - - - - - - - - - - - - - -\n";
return os; return os;
} }
// mostly a helper function to ease the conversion from timepoint to int
// for storing in the db
int Park_time::start_to_int() { int Park_time::start_to_int() {
auto start_to_epoch = start.time_since_epoch(); auto start_to_epoch = start.time_since_epoch();
auto start_value = duration_cast<seconds>(start_to_epoch); auto start_value = duration_cast<seconds>(start_to_epoch);
@ -64,6 +80,9 @@ int Park_time::start_to_int() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void Park_time::save_db() { void Park_time::save_db() {
/*
this creates a sql statement and then executes it
*/
string statement{"insert into Park_time values ( , , , , , );"}; string statement{"insert into Park_time values ( , , , , , );"};
statement.insert(41, "NULL"); statement.insert(41, "NULL");
statement.insert(39, "NULL"); statement.insert(39, "NULL");
@ -75,7 +94,7 @@ void Park_time::save_db() {
data::db.exec(statement); data::db.exec(statement);
transaction.commit(); transaction.commit();
} }
// same as above
void Park_time::update_db() { void Park_time::update_db() {
string statement = string statement =
"UPDATE Park_time SET end = , duration = where id = '';"; "UPDATE Park_time SET end = , duration = where id = '';";

View File

@ -78,8 +78,10 @@ Customer query_customer_with_id(int id) {
// -------------- paroking spots // -------------- paroking spots
// vector<Park_spot> query_all_parking_spots() {
// vector<Park_spot> spots;
// vector<Park_spot> populate_spots(){
// vector<Park_spot> spots;
// SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;"); // SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;");
// // query.bind(1, 2); // // query.bind(1, 2);
// while (query.executeStep()) { // while (query.executeStep()) {
@ -90,20 +92,4 @@ Customer query_customer_with_id(int id) {
// spots.push_back({id, taken, cid}); // spots.push_back({id, taken, cid});
// } // }
// return spots; // return spots;
// } // }
vector<Park_spot> populate_spots(){
vector<Park_spot> spots;
SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;");
// query.bind(1, 2);
while (query.executeStep()) {
int id = query.getColumn(0);
int taken = query.getColumn(1);
int cid = query.getColumn(2);
// park_customers.push_back(query_customer_with_id(cid));
spots.push_back({id, taken, cid});
}
return spots;
}

View File

@ -3,15 +3,25 @@
namespace data { namespace data {
SQLite::Database start_db() { SQLite::Database start_db() {
/*
Opens the database, creates it if it can't find the file.
*/
SQLite::Database db("test.db3", SQLite::Database db("test.db3",
SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
while (sodium_init() < 0) { while (sodium_init() < 0) {
std::cout << "SODIUM NOT WORKING"; std::cout << "SODIUM NOT WORKING";
/*
This shouldn't be here, really, but I can't think of a better place
where it runs at least once. This seeds the random generator needed for
salts and other stuff, and needs to be run at least once when working
with any libsodium function.
*/
} }
//sql syntax is surprisingly readable.
db.exec( db.exec(
"create table if not exists Customer (id integer primary key, name " "create table if not exists Customer (id integer primary key, name "
"text, password text, verhicle int)"); "text, password text, verhicle int)");
// getting errors when using bool, so i used an int instead.
db.exec( db.exec(
"create table if not exists Park_spot (id integer primary key, taken " "create table if not exists Park_spot (id integer primary key, taken "
"int, customer_id int)"); "int, customer_id int)");

View File

@ -1,16 +1,19 @@
#include "headers/encrypt.h" #include "headers/encrypt.h"
string hash_password(string password) { string hash_password(string password) {
/* /*
Passing strings and converting to char* because I do not want to be forced Passing strings and converting to char* because I do not want to be forced
to use char * whenever I want to call the function. Low level stuff in the to use char * whenever I want to call the function. Low level stuff in the
function, the least possible low level stuff outside. function, the least possible low level stuff outside.
This uses the password hashing algorithm Argon2 implemented by libsodium.
DO NOT MODIFY memory_limit and cpu_limit after you add customers to the db.
When you do that, the hashed passwords can't be decrypted, and that would be
BAD
*/ */
const char* password_ = password.c_str(); const char* password_ = password.c_str();
char hashed_password_[crypto_pwhash_STRBYTES]; char hashed_password_[crypto_pwhash_STRBYTES];
int memory_limit = 3.2e+7; // 3.2e7 = 32e6 = 32 mb int memory_limit = 3.2e+7; // 3.2e7 = 32e6 = 32 mb
int cpu_limit = 1; // this is n_threads int cpu_limit = 1; // this is n_threads
int result = crypto_pwhash_str(hashed_password_, int result = crypto_pwhash_str(hashed_password_,
password_, password_,
@ -23,6 +26,9 @@ string hash_password(string password) {
} }
bool verify_password(string hashed_password, string unhashed_password) { bool verify_password(string hashed_password, string unhashed_password) {
/*
this verifies the password. It's encryption magic and don't question it.
*/
const char* password_ = unhashed_password.c_str(); const char* password_ = unhashed_password.c_str();
const char* hashed_password_ = hashed_password.c_str(); const char* hashed_password_ = hashed_password.c_str();

View File

@ -9,17 +9,29 @@
using std::vector; using std::vector;
// will make it easy to represent it in the database while making it easy to use /*
// while programming enum classes make it easy to represent categories.
So you can use something like Verhicle_type::car instead of 2. but under the
hood, it's still an int. This is here so you won't have to have global variables
for these categories, or worse, use magic numbers in the code.
*/
enum class Verhicle_type { bike = 1, small_car = 2, suv = 3, pickup = 4 }; enum class Verhicle_type { bike = 1, small_car = 2, suv = 3, pickup = 4 };
/* /*
card code is een randomly generated string moeten zijn, die je bv. op een nfc Customer constructors do the same stuff as all the other constructors.
card zou opslaan en zo zou authenticaten bij je parking spot. We kunnen dit ipv clock_in and out create and modify park_time objects and store them to
of samen met een password gebruiken. clock in en out creeert en compleet een park_instances. Technically, now that we have a working db, we don't need it.
park_time object. Voegt het toe aan een vector. TODO: fix this.
*/ gen_monthly just prints out all the park_time objects in park_instances.
It should (and can safely) be removed, but it's here as a quick example of
report generation It has no logic to speak of that only generates report of
ptime objects of this month.
TODO: remove when have seperate report generation functions.
save, update, delete and auto increment are the same as in park_time.
*/
class Customer { class Customer {
public: public:
@ -27,9 +39,7 @@ class Customer {
string name; string name;
string password; string password;
Customer(string name_, string password_, Verhicle_type verhicle_); Customer(string name_, string password_, Verhicle_type verhicle_);
Customer(int id_, string name_, // needed to construct from db Customer(int id_, string name_, string password_, Verhicle_type verhicle_,
string password_,
Verhicle_type verhicle_, // TODO: how init. p_time instances?
vector<Park_time> instances); vector<Park_time> instances);
void clock_in(int s_id); void clock_in(int s_id);
void clock_out(int s_id); void clock_out(int s_id);
@ -37,7 +47,7 @@ class Customer {
void update_db(); void update_db();
void delete_db(); void delete_db();
void gen_monthly(); // remove, make it a function in data void gen_monthly();
Verhicle_type verhicle; Verhicle_type verhicle;
private: private:
@ -46,5 +56,4 @@ class Customer {
int auto_increment_db(); int auto_increment_db();
}; };
#endif // CUSTOMER_H #endif // CUSTOMER_H

View File

@ -17,6 +17,28 @@ using std::to_string;
Record of who parked at what park_spot and at what time. Record of who parked at what park_spot and at what time.
public interface-------------------------------------------
The constructors. one for creating new customers, the other one used by the
query functions to construct the object from information stored in the database.
clock_out is the function that gets called from customer.clock_out().
It verifies that the customer is clocking out at the correct parkspot, and saves
the current time of clocking out in end. It also calculates duration so it
doesn't have to be calculated more than once.
operator<< is << overload, can(should) be used for report generation.
// implementation stuff------------------------
start and end are time points representing when someone clocks in and out. they're from the chrono namespace.
save and update save and update info in the database.
auto_increment pulls the highest id stored in the db, to be used in the constructor.
start_to_int() is used to convert the start timepoint to an integer that can be saved in the database
SQL datetime and chrono datetime don't seem the most compatible.
*/ */
class Park_time { class Park_time {

View File

@ -4,17 +4,57 @@
#include "Park_spot.h" #include "Park_spot.h"
#include <array> /*these are the functions that search the database and create objects from it.
query_parktimes_for_customer searches for the parktimes that are needed in
customer initialisaiton. generally, i see no use outside of that.
query_customer_with_name searches for customer data by name.
query_customer_with_id does what the above does, but with id.
populate_spots is used to query for all the park_spots and return them as
objects.
The design desision to use vector<T> instead of <T> is for the following
reasons:
1. some of these can potentially return more than one object. For example, 2
customers who have the same name.
2. I have no clue how many of you have done error handling in c++
(try/catch/finally).
Ya boi is nice and doesn't want to bombard you with more new concepts than needed.
so now you'd do
vector<Customer> test = query_customer_with_name("Testman");
if (!test.size()) {print no customers found, do stuff}
else if (test.size() > 1) { do stuff to get the right one if you only need one
}
instead of
try {
customer test = query_customer_with_name("Testman");
}
catch(someException.probablycalled_not_found) {do_Stuff};
catch(...) {
do stuff
}
finally{
do more stuff
}
3. Ya boi needs to brush up on how to create custom exceptions class, and it will complicate code furhter.
*/
vector<Park_time> query_parktimes_for_customer(int cid); vector<Park_time> query_parktimes_for_customer(int cid);
vector<Customer> query_customer_with_name(string name); vector<Customer> query_customer_with_name(string name);
Customer query_customer_with_id(int id); Customer query_customer_with_id(int id);
// vector<Park_spot> query_all_parking_spots(); // used for initializing the parking spots at start of the program
vector<Park_spot> populate_spots(); vector<Park_spot> populate_spots();
#endif // CUSTOMER_H #endif // CUSTOMER_H

View File

@ -5,8 +5,13 @@
#include "encrypt.h" #include "encrypt.h"
namespace data { namespace data {
SQLite::Database start_db();
/*
start_db is the function that opens the database, and
if the necesary tables are not there, creates them.
db is the database, and is static to avoid multiple redefinition errors.
*/
SQLite::Database start_db();
static SQLite::Database db = start_db(); static SQLite::Database db = start_db();
} // namespace data } // namespace data

View File

@ -2,12 +2,20 @@
#define ENCRYPT_H #define ENCRYPT_H
#pragma once #pragma once
#include <string>
#include <cstring> #include <cstring>
#include <sodium.h>
#include <iostream> #include <iostream>
#include <sodium.h>
#include <string>
using std::string; using std::string;
/*
hash_password takes the password, and encrypts it. This needs to be done,
because storing passwords in plaintext is BAD!
verify_password takes in a password and the hashed password, and then does magic encryption stuff(no, not
really. It basically hashes the password with the same salt and other
parameters) and to see if the password stored and the given password match.
*/
string hash_password(string password); string hash_password(string password);
bool verify_password(string hashed_password, string unhashed_password); bool verify_password(string hashed_password, string unhashed_password);

108
main.cpp
View File

@ -1,65 +1,103 @@
#include "headers/Query.h" #include "headers/Query.h"
#include <chrono>
#include <thread> #include <thread>
using namespace std::chrono; using namespace std::chrono;
/* /*
Code strucure like this: Code structure is like this:
class declarations zijn in /headers/class_naam.h, en definitions van de member 1. encrypt.cpp en /header/encrypt.h contain functions to hash passwords and
functs in /class_naam.cpp elke klas in zn eigen file omdat ik incomplete class verify passwords
declarations wilt tegengaan, omdat ik ze niet goed begrijp. En header/source
split om multiple definition errors tegen te gaan.
Park_spot representeert een parkeermeter bij elke parkeer spot. 2. data.cpp and /header/data.h contain the code to start up the database.
Een customer is een customer. Originally, they were supposed to contain all the functions to save to the
Park time is een object die reffereert naar parkspot en customer, basically een database and query from the database. I had trouble doing that, (cyclical
record die zegt dat een customer voor x tijd geparkeert heeft bij spot x, enz. includes) and some other issues. the other issues are gone due to the latest
refactor, but to make it like my original plan is going to take a few hours, and
I have done too much already to want to do more work unless needed.
The functions to save to a database have been integrated in the classes
themself, and unless issues arrise from that I'm not changing that. Functions to
get objects from the database are in Query.cpp en header.
De client clockt in en uit bij een spot. 3. Park_time.cpp en header.
Contain the implementation details of Park_time, which is basically a record of
who parked at what spot and when. Uses a mix of ctime and chrono functions to do
most of the stuff, it's a mess. I will probably have to commit to Doing it one
way or the other to make it more comperhensible, especially for whoever will
make report functions.
4. Customer.cpp and header.
Contains the implementation of Customer. Customer represents a customer, and
saves park_time instances in itself. Not much to explain.
5. Park_spot.cpp and header.
It contians the implementation details of Park_spot, which represents it's
namesake.
6. Query.cpp and header.
Cointain functions that search the database and return objects(P_time, P_spot,
Customer) It is the least tested of the whole project, use with care.
Explanation of what members do of P_time, P_spot, Customer are in the respective
headers. Explanations of how the member functions work(Or how I intended for
them to work) are in the respective .cpp files. void Wait(int sec)
*/ */
void Wait(int sec) void Wait(int sec)
/*
a wait function where 1 sec represents 1 hour irl.
*/
{ {
/*
a wait function where 1 sec represents 1 hour irl. It has been used for testing
purposes mostly.
TODO: Needs to be removed at completion of project, or seperated in a test
cpp/header
*/
std::this_thread::sleep_for(seconds{sec}); std::this_thread::sleep_for(seconds{sec});
} }
static vector<Park_spot> parking_spots = populate_spots(); // to save the parking spots in memory static vector<Park_spot> parking_spots = populate_spots();
static vector<Customer> park_customers; // save the customers that are parked in here // this queries the db for all the saved parking_spots and initializes them
static vector<Customer> park_customers;
/*
This was meant for an older implementation. park_time objects used to store
pointers to customers and in order to not get dangling pointers(dangerous!) I
had to have a way to store the customers the pointer pointed to so they didn't
get destroyed prematurely(I could've used the lower-level, more dangerous new,
or worse, malloc, but that's ugly).
For now, it's just here in case you want an easy way to store customers.
*/
int main() { int main() {
Customer sagar = query_customer_with_name("stefan udit")[0]; Customer sagar = query_customer_with_name("stefan udit")[0];
Customer sagar1 = query_customer_with_id(2); Customer sagar1 = query_customer_with_id(2);
cout << sagar.id << "," << sagar.name << "," << sagar.password<< "\n"; cout << sagar.id << "," << sagar.name << "," << sagar.password << "\n";
cout << sagar1.id << "," << sagar1.name << "," << sagar1.password; cout << sagar1.id << "," << sagar1.name << "," << sagar1.password;
cout << parking_spots.size(); cout << parking_spots.size();
for (auto i : parking_spots){ for (auto i : parking_spots) {
cout << "\n" << i.id << "," << i.parked_customer; cout << "\n" << i.id << "," << i.parked_customer;
} }
populate_spots(); populate_spots();
} }
/*
Why is this not in query.cpp? Because somehow, it errors out when it's there.
The error message indicates it is a memory issue but I suspect it's a
// vector<Park_spot> populate_spots(){ concurrency issue. Do not move this.
// vector<Park_spot> spots; */
// SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;"); vector<Park_spot> populate_spots() {
// // query.bind(1, 2); vector<Park_spot> spots;
// while (query.executeStep()) { SQLite::Statement query(data::db, "SELECT * FROM Park_spot WHERE id > 0;");
// int id = query.getColumn(0); // query.bind(1, 2);
// int taken = query.getColumn(1); while (query.executeStep()) {
// int cid = query.getColumn(2); int id = query.getColumn(0);
// // park_customers.push_back(query_customer_with_id(cid)); int taken = query.getColumn(1);
// spots.push_back({id, taken, cid}); int cid = query.getColumn(2);
// } // park_customers.push_back(query_customer_with_id(cid));
// return spots; spots.push_back({id, taken, cid});
// } }
return spots;
}