RestaurantInformation.cpp
------------------------------------------------
Restaurant and Sitting Class Implementation File
(contains Reservation Structure implementation)
------------------------------------------------*/
#include "stdafx.h"
#include "RestaurantInformation.h"
/*-------------------
Reservation Structure
---------------------*/
//constructors (default followed by overloaded with 3 arguments):
Reservation::Reservation(){which_sitting=0;under_what_name="";total_in_party=0;}
Reservation::Reservation(int w_s, String^ u_w_n, int t_i_p) {which_sitting = w_s-1; under_what_name = u_w_n; total_in_party = t_i_p;};
/*-----------
Sitting Class
-------------*/
//constructor:
Sitting::Sitting(String^ sn){bookings = gcnew Hashtable(); sitting_name = sn;};
//destructor:
Sitting::~Sitting(){delete bookings;};
/*--------------
Restaurant Class
----------------*/
//constructor:
Restaurant::Restaurant(){
restaurant_sitting = gcnew array<Sitting ^>(num_of_sittings);
for (int count = 0; count < num_of_sittings; count++)this->restaurant_sitting[count] = gcnew Sitting("Sitting "+(count+1));
};
//destructor:
Restaurant::~Restaurant(){
delete restaurant_sitting;
};
/*seatsAvailableMessage - returns a string containing how many seats have yet to be booked
(as a proportion of total seats available) - */
String^ Restaurant::seatsAvailableMessage(){
String^ seatsAvailable;
for (int count = 0; count < num_of_sittings; count++){
seatsAvailable = seatsAvailable + this->restaurant_sitting[count]->sitting_name+": Only "
+System::Convert::ToString(count_num_places_avail(count))+" of "
+System::Convert::ToString(num_of_seats)+" total seats left.\n";
}
return seatsAvailable;
}
//welcomeMessage: returns a string with the welcoming concatenated with the seats available message -
String^ Restaurant::welcomeMessage(){
String^ welcome = "\nWelcome to Christmas Restaurant!\nNow taking bookings for:\n";
welcome = welcome + seatsAvailableMessage();
return welcome;
}
//deleteBooking: thread method that takes a Reservation Key (String^) as an argument and removes it from the bookings -
void Restaurant::deleteBooking(Object^ o){
String^ t = (String^) o;
/* Necessary to use a mutual exclusion lock so that only one thread can execute the following
code at once. This is necessary to avoid anomalies when updating this Restaurant shared place availability.
The lock is placed on this Restaurant object (i.e. this), and thus it cannot be used by another thread
until Monitor::Exit below.*/
Monitor::Enter(this);
//SIMULATE 5 SECOND DELAY CORRESPONDING TO THE PROCESSING OF THIS REQUEST:
Thread::Sleep(5000);
try { //to update places available:
for (int count = 0; count < num_of_sittings; count++){ //for each sitting:
//use an enumerator to parse the Hashtable for each sitting:
IDictionaryEnumerator^ e1 = this->restaurant_sitting[count]->bookings->GetEnumerator();
//while bookings exist:
while (e1->MoveNext()){
//we need the KEY to the Hashtable (which is the unique reservation key):
String^ currently_being_processed = e1->Key->ToString();
//if we have a match:
if(currently_being_processed->Equals(t->ToUpper())){
//remove it!
this->restaurant_sitting[count]->bookings->Remove(currently_being_processed);
break; //exit - saves time as we don't need to parse the rest of the bookings
}
}//the finish of the loop that continues while bookings exist
}//the end of the for loop that processes each sitting
//OK, now more places will be available (provided correct KEY) - so pulse the one (and one only) thread possibly waiting below (addBooking):
Monitor::Pulse(this);
}//end of trying to update the place availability structure.
finally {
//...so we can now unlock the place availability structure object from use (which is this Restaurant object):
Monitor::Exit(this);
}
};
//addBooking: thread method that takes a Reservation as an argument and tries to add it to the bookings -
void Restaurant::addBooking(Object^ o){
Reservation^ t = (Reservation^) o;
//Mutual exclusion lock, as per the remove bookings - to avoid logical anomalies (only 1 thread can process this at a time):
Monitor::Enter(this);
//5 SECOND DELAY CORRESPONDING TO THE PROCESSING OF THIS REQUEST:
Thread::Sleep(5000);
try {
// Waiting on a condition - for places to become available:
while (this->count_num_places_avail(t->which_sitting) < t->total_in_party) Monitor::Wait(this);
//above line waits for PULSE - maybe someone will remove a booking?
this->save_reservation(t); //OK, it happened - there are more places available, so continue thread.
}
finally {
Monitor::Exit(this); //remove the lock
}
};
//feedback_reservation_key: takes a reservation as an argument, then locates (and returns) the corresponding KEY for this reservation -
String^ Restaurant::feedback_reservation_key(Reservation ^ this_reservation){
//use an enumerator to parse the bookings:
IDictionaryEnumerator^ e1 = this->restaurant_sitting[this_reservation->which_sitting]->bookings->GetEnumerator();
//while bookings exist:
while (e1->MoveNext()){
//create a temporary variable to hold the reservation information:
Reservation^ currently_being_processed = gcnew Reservation();
//set the value of the temporary variable to the enumerator VALUE:
currently_being_processed = (Reservation^)(e1->Value);
//if the VALUES match (the currently processed and the searched for value) return the KEY:
if(currently_being_processed==this_reservation) return System::Convert::ToString(e1->Key)->ToUpper();
}
/*this shouldn't happen in the context of this implementation file, as this method
is in its current state this method is only called after a successful booking.
Sill, for future implementations, this is seen as a necessary step: */
return "Booking does not exist. Please contact the restaurant.";
}
/*save_reservation: takes a reservation as an argument to store in the shared data structure -
Note: in this Classes current form, this is invoked from the addBooking thread - and thus will
only by executed by one thread at a time, as per the mutual exclusion conditions imposed by this thread (above): */
void Restaurant::save_reservation(Reservation ^ i){
String^ reservation_key;
//concatenate the unique identifier, sitting, name and total in party to make a unique KEY:
reservation_key = System::Convert::ToString(this->unique_reservation_key)+
System::Convert::ToString(i->which_sitting+1)+System::Convert::ToString(i->under_what_name)+
System::Convert::ToString(i->total_in_party);
//remove spaces from key:
reservation_key = reservation_key->Replace(" ","");
//using the Hashtable->Add(KEY,VALUE) - store both the key->toUpperCase and the reservation (i):
this->restaurant_sitting[i->which_sitting]->bookings->Add(reservation_key->ToUpper(),i);
//increment the unique key:
this->unique_reservation_key++;
}
//count_num_places_avail: inputs the sitting (number), returns places available (as an integer) -
int Restaurant::count_num_places_avail(int of_sitting){
int total_number_sitting = 0;
//use an enumerator to parse bookings Hashtable:
IDictionaryEnumerator^ e1 = this->restaurant_sitting[of_sitting]->bookings->GetEnumerator();
//while bookings exist:
while (e1->MoveNext()){
//create a temporary variable to hold the reservation information:
Reservation^ currently_being_processed = gcnew Reservation();
//set the value of the temporary variable to the enumerator VALUE:
currently_being_processed = (Reservation^)(e1->Value);
//add the value of total_in_party to the total_number_sitting value:
total_number_sitting+=currently_being_processed->total_in_party;
}
//return this total:
return num_of_seats-total_number_sitting;
}
/*generateConsoleBookingReport - for use in the Server Console window - redisplays every 60 seconds the information
recorded at that time. Although not a requirement, this is a clear indicator of the entire solution working,
and also may primarily benefit the restaurant or guide further UI implentation in the future. */
void Restaurant::generateConsoleBookingReport(){
//underline() - user defined - see below:
underline();
Console::ForegroundColor::set(System::ConsoleColor::Yellow);
//padRight - user defined - see below:
padRight("Reservation Key",29);
padRight("Sitting",7);
padRight("Under Name",29);
padRight("Total",5);
Console::WriteLine("|");
Console::ForegroundColor::set(System::ConsoleColor::Gray);
underline();
//for each sitting:
for (int count = 0; count < num_of_sittings; count++){
IDictionaryEnumerator^ e1 = this->restaurant_sitting[count]->bookings->GetEnumerator();
int totalSittingReservations = 0;
//if there are no bookings:
if(this->restaurant_sitting[count]->bookings->Count==0){
Console::ForegroundColor::set(System::ConsoleColor::Red);
padRight("No reservations yet for Sitting "+System::Convert::ToString((count)+1),73);
Console::WriteLine("|");
}
else
{ //while there are bookings, display all the information from each one:
while (e1->MoveNext()){
Console::ForegroundColor::set(System::ConsoleColor::Magenta);
Reservation^ current_reservation_being_processed = gcnew Reservation();
current_reservation_being_processed = (Reservation^)(e1->Value);
String^ current_key_being_processed = e1->Key->ToString();
padRight(current_key_being_processed,29);
padRight(System::Convert::ToString((current_reservation_being_processed->which_sitting)+1),7);
padRight(current_reservation_being_processed->under_what_name,29);
totalSittingReservations=totalSittingReservations+current_reservation_being_processed->total_in_party;
padRight(System::Convert::ToString(current_reservation_being_processed->total_in_party),5);
Console::WriteLine("|");
}
}
//a sitting is complete - round it off with the total number of patrons sitting within that specific sitting:
Console::ForegroundColor::set(System::ConsoleColor::Gray);
underline();
Console::ForegroundColor::set(System::ConsoleColor::Green);
Console::Write("|");
String^ str="Total Patrons Sitting "+System::Convert::ToString((count)+1)+ ": "+System::Convert::ToString(totalSittingReservations);
Console::Write(str->PadLeft(73,' '));
Console::WriteLine("|");
Console::ForegroundColor::set(System::ConsoleColor::Gray);
underline();
}//repeat this for each sitting
}
//padRight - aligns text for table formatting in Server Console window
void Restaurant::padRight(String^ toLeftAlign, int distance){
char withWhat = ' ';
Console::Write("|");
Console::Write(toLeftAlign->PadRight(distance, withWhat));
};
//underline - produces one row of 75 hyphens across Console Window:
void Restaurant::underline(){
for(int hyphencount=1; hyphencount<=74; hyphencount++)Console::Write("-");Console::WriteLine("-");
};