Digital Solutions



Standard response:



Something wrong:


Potential causes: DDoS, VPS node/cluster full, high resource usage on server, hardware fault, Apache2 / Ubuntu config
Algorithm
There is no cloud
Stack Overflow

that went nowhere.

¯\_(ツ)_/¯
THE PLAN | THE REVIEW

Museum information kiosk plan | manual | quality assurance | evaluation

Cheque Writer pdf | test code online

Reservation client server architecture plan | evaluation




------------------------------------
Reservation Client Main Project File
------------------------------------*/

#include "stdafx.h"
#include "TcpClient.h"

int main(array<System::String ^> ^args){
	String^ request;
	TcpConsole client;
	//if the client cannot connect:
	if(!client.connect())return 0;
	//get connecting messages:
	client.read_block();
	while (true) //connected:
		{
		client.menu_options();		
		//get initial choice:
		request = Console::ReadLine();
		//quit:
		if (request->ToUpper()->Equals ("Q")){
			//let the server know:
			client.writer->WriteLine("DISCONNECT");
			client.writer->Flush();
			break;
		}
		else client.prepare_request(request); //invoke method that decides how to handle request
		try	{client.send_request(); //send outgoing request
			 client.process_response(); //receive and process incoming request
			}
		catch (IOException^){break;} // incase connection is lost
	}
	return 0;
}

---------------------------------
TcpConsole Class Declaration File
---------------------------------*/

#ifndef _TCPCLIENT_H
#define _TCPCLIENT_H

#include "stdafx.h"

using namespace System;
using namespace System::IO;  //for StreamWriter and StreamReader Classes, etc.
using namespace System::Net;  //for protocol-related classes.
using namespace System::Net::Sockets;  //for managed implementation of Winsock interface.

/***************************************************************************************
TcpConsole Class: used to manage client console for interaction with reservation server.
****************************************************************************************/
ref class TcpConsole
{
private:
	//variables:
	bool failedFlag;
	TcpClient^ server; //Provides client connection for reservation service, and enables stream communication with this connection.
	bool expecting_unknown_length_answer;
	StreamReader^ reader; //Created from Networkstream object to handle incoming stream
	String^ write_buffer;
	String^ read_buffer;
	//functions:
	void read_unknown_length();
	
public:
	//constructor:
	TcpConsole();
	//destructor:
	~TcpConsole();
	//variables:
	StreamWriter^ writer; //as StreamReader (above) but to handle outgoing stream
	//functions (see implementation file for description of each):
	bool connect();
	void read_block();
	void menu_options();
	void prepare_request(String ^);
	void send_request();
	void process_response();
};

#endif

------------------------------------
TcpConsole Class Implementation File
------------------------------------*/

#include "stdafx.h"
#include "TcpClient.h"

/***********
constructor:
************/
TcpConsole::TcpConsole(){
	Console::WriteLine("***** Restaurant Booking Client *****\n");
	expecting_unknown_length_answer = false;
};

/**********
destructor:
***********/
TcpConsole::~TcpConsole(){
	//if no flag on connection:
	if(!failedFlag){
		//Mark the TcpClient instance as disposed, and request the socket to close the TCP connection:
		server->Close();
		Console::WriteLine("Ended connection with server.");
		//Clean up dynamically allocated resources:
		delete server; delete writer; delete reader; delete write_buffer; delete read_buffer;
	}
};

/*************************************************
connect: using a connection oriented socket (TCP),
allows client console to connect to TCP Server.
**************************************************/
bool TcpConsole::connect(){
	try //and make this connection:
		{
		/*Following example is using IP 127.0.0.1 for standard loopback connection
		during development (this MUST be changed to server IP upon publishing).
		Port 12345 is port being listened by server (specified in ReservationServer.cpp)
		- thus client MUST use this port or server will actively refuse the connection.
		TCP chosen as protocol due to its guaranteed delivery of payload (data)
		unlike media / gaming applications primarily concerned with speed (where UDP would be of greater use).
		Further discussion of this found in various materials on the web.*/
		server = gcnew TcpClient("127.0.0.1", 12345);
		/*Using a stream reader and writer to make client / server
		communication easier with server through a "base stream" of strings.
		GetStream returns a Networkstream object from the current server,
		from which the Stream Reader and Writer are created:*/
		writer = gcnew StreamWriter(server->GetStream());
		reader = gcnew StreamReader(server->GetStream());
		}	
	//...and if this connection fails:
	catch (SocketException^ se)
		{
		//Display the error message thrown:
		Console::WriteLine("Could not connect to server: "+se->Message+"\nHit enter to exit.");
		//give users chance to read message:
		Console::ReadLine();
		failedFlag = true;
		return false;
		}
	failedFlag = false;
	return true;
};

/************************************************
read_block: used to read from the input base
stream until there is nothing left in the stream.
*************************************************/
void TcpConsole::read_block(){
	//while there is another available character in the input stream, write it:
	while (reader->Peek()>=0)Console::WriteLine(reader->ReadLine());
	//once all characters have been read, clear internal buffer:
	this->reader->DiscardBufferedData();
};

/*********************************
menu_options: provides console UI.
**********************************/
void TcpConsole::menu_options(){
	//MENU:
	Console::WriteLine("\nOptions:");
	Console::WriteLine("hit '1' to request the number of places currently available;");
	Console::WriteLine("hit '2' to make a booking;");
	Console::WriteLine("hit '3' to cancel a previous booking;");
	Console::WriteLine("hit 'q' to quit.");
	Console::WriteLine();
	Console::Write("Your Choice: ");
};

/****************************************************************************
prepare_request: accepts a String^ argument which contains the users request,
and concatenates all information supplied into a single line ready for the
write buffer.  Finally, determines length of response.
*****************************************************************************/
void TcpConsole::prepare_request(String^ msg){
	write_buffer="";

	//1. display bookings:
	if(msg->Equals ("1")) {write_buffer="1"; expecting_unknown_length_answer = false;}
	
	//2. make booking:
	if (msg->Equals ("2")){
		write_buffer="2,";
		Console::Write("Which sitting number? eg. '1': ");
		write_buffer=write_buffer+Console::ReadLine();
		Console::Write("What booking name? eg. 'George': ");
		write_buffer=write_buffer+","+Console::ReadLine();
		Console::Write("How many seats? eg. '2': ");
		write_buffer=write_buffer+","+Console::ReadLine();
		Console::WriteLine("\nProcessing... please be patient...\n");
		expecting_unknown_length_answer = true;
	}
		
	//3. cancel booking:
	if (msg->Equals ("3")){
		write_buffer="3,";
		Console::Write("What was your UNIQUE RESERVATION KEY? eg. '82HARRY3': ");
		write_buffer=write_buffer+Console::ReadLine();
		Console::WriteLine("\nProcessing... please be patient...\n");
		expecting_unknown_length_answer = false;
	}
};

/****************************************************************
send_request: sends underlying streamwriter buffer to the server.
*****************************************************************/
void TcpConsole::send_request(){
	/*just incase there is anything residual, as after flushing the write buffer, 
	a response could be expected at minimum instantly:*/
	reader->DiscardBufferedData();
	//send write buffer to the stream writer and flush it (send it to the server):
	writer->WriteLine(write_buffer);
	writer->Flush();
};

/***************************************************************************
process_response: determines whether response is a fixed or variable length.
****************************************************************************/
void TcpConsole::process_response(){
	if(!expecting_unknown_length_answer) read_block(); else	read_unknown_length();		
};

/*********************************************************************
read_unknown_length: continues reading stream until encountering "EOM"
which signals the end of message.
**********************************************************************/
void TcpConsole::read_unknown_length(){
	for(;;){
			read_buffer="";
			read_buffer=reader->ReadLine();
			if(read_buffer=="EOM")break;
			//for unique reservation key, set console colour to magenta so it stands out:
			if(read_buffer=="EXPECT_KEY") Console::ForegroundColor::set(System::ConsoleColor::Magenta); else Console::WriteLine(read_buffer);
			}
	Console::ForegroundColor::set(System::ConsoleColor::Gray);
	this->reader->DiscardBufferedData();
};


------------------------------------
Reservation Server Main Project File
------------------------------------*/

#include "stdafx.h"
#include "TcpServer.h"

int main(array<System::String ^> ^args)
{
	Console::WriteLine("***** Restaurant Booking Server *****");
	Console::WriteLine("Commenced server session "+DateTime::Now+"\n");
	TcpServer^ server = gcnew TcpServer();
	/*create a delegate to the method that is the starting point of the thread.  Thread method is fully qualified in argument.
	Note - this thread takes no arguments - hence use of ThreadStart and not ParameterizedThreadStart */
	ThreadStart^ redisplayBookingsStart = gcnew ThreadStart(server, &TcpServer::redisplayBookingsEverySixtySeconds);
	//create the thread:
	Thread ^redisplayBookingsThread = gcnew Thread(redisplayBookingsStart);
	//start the thread:
	redisplayBookingsThread->Start();
	//create a new listener for connections from TCP network clients (from any IP address):
	TcpListener^ socket = gcnew TcpListener(IPAddress::Any, 12345);
	//starts socket listening for incoming connection requests:
	socket->Start();
	while(true)
	{
		Console::WriteLine("Waiting for client connection...");
		//Accept a pending connection request and assign it to a new TcpClient Class object, ready for sending and receiving:
		TcpClient ^ client = socket->AcceptTcpClient();
		//create a new parameterized thread delegate (this will be used for the connecting client):
		Thread ^thr = gcnew Thread(gcnew ParameterizedThreadStart(server, &TcpServer::ProcessThread));
		//start the new thread for the connecting TcpClient object by supplying the object as an argument to the thread (i.e. client):
		thr->Start(client);
	}
	return 0;
}

---------------------------------------------
Restaurant and Sitting Class Declaration File
(contains Reservation Structure declaration)
---------------------------------------------*/

#ifndef _RESTAURANTINFORMATION_H
#define _RESTAURANTINFORMATION_H

#include "stdafx.h"

using namespace System;
using namespace System::Collections;  //for Hashtable, etc
using namespace System::Threading;  //for Thread management

/******************************************************
Constant definitions: change these to suit restaurant -
*******************************************************/
#define SEATS_AVAILABLE_IN_RESTAURANT 30
#define NUMBER_OF_SITTINGS_TO_TAKE_BOOKINGS_FOR 2

/*********************************************************
Reservation Structure: used to hold data for reservations.
**********************************************************/
public ref struct Reservation {
	int which_sitting;
	String^ under_what_name;
	int total_in_party;
	Reservation(int, String^, int);
	Reservation();
};

/******************************************************
Sitting Class: A restaurant may have multiple sittings.
A bookings Hashtable will be used for each sitting to
store reservations.
*******************************************************/
public ref class Sitting
{
	public:
	String^ sitting_name;
	Hashtable^ bookings;
	Sitting(String^);
	~Sitting();
};

/*****************************************************
Restaurant Class: Provides necessary data structures
and methods for server to retrieve, process, store and
remove information on bookings.
******************************************************/
public ref class Restaurant
{
	private:
		//variables:
		static int num_of_sittings = NUMBER_OF_SITTINGS_TO_TAKE_BOOKINGS_FOR;
		static int num_of_seats = SEATS_AVAILABLE_IN_RESTAURANT;
		static int unique_reservation_key = 1; //increments by 1 for each booking taken
		array<Sitting^>^ restaurant_sitting; //an array of Sittings (each Sitting has its own Hashtable for bookings)

		//functions:
		void padRight(String^, int);
		void underline();
		void save_reservation(Reservation^);
		int count_num_places_avail(int);
		
	public:
		//constructor:
		Restaurant();

		//destructor:
		~Restaurant();

		//threads:		
		void addBooking(Object^);
		void deleteBooking(Object^);
		
		//methods:
		String^ feedback_reservation_key(Reservation^);
		String^ welcomeMessage();
		String^ seatsAvailableMessage();
		void generateConsoleBookingReport();
};


#endif

------------------------------------------------
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("-");
};

--------------------------------
TcpServer Class Declaration File
--------------------------------*/

#ifndef _TCPSERVER_H
#define _TCPSERVER_H

#include "stdafx.h"
#include "RestaurantInformation.h"

using namespace System;
using namespace System::IO;  //for StreamWriter and StreamReader Classes, etc.
using namespace System::Net;  //for protocol-related classes.
using namespace System::Net::Sockets;  //for managed implementation of Winsock interface.
using namespace System::Threading;  //for thread management.

/***********************************************************************
TcpServer Class: used to manage client processes for reservation server.
See implementation file for description of each thread / function.
************************************************************************/
ref class TcpServer
{	
	private:
		//structures:
		value struct dissected {int seats; String^ who; int sitting;};
		//variables:
		Restaurant^ christmaslunch;
		//functions:
		dissected dissect_msg_string(String^);
		void requestPlaces(StreamWriter^);
		void makeBooking(StreamWriter^, String^);
		void cancelBooking(StreamWriter^, String^);
	
	public:
		//constructor:
		TcpServer();
		//destructor:
		~TcpServer();
		//threads:
		void ProcessThread(Object^);
		void redisplayBookingsEverySixtySeconds();
};

#endif

-----------------------------------
TcpServer Class Implementation File
-----------------------------------*/

#include "stdafx.h"
#include "TcpServer.h"

/***********
constructor:
************/
TcpServer::TcpServer(){christmaslunch = gcnew Restaurant();};

/**********
destructor:
***********/
TcpServer::~TcpServer(){delete christmaslunch;};

/***********************************************************
dissected: accepts an argument String^ and separates it into
its respective values ready for reservation processing. It
returns this final dissected value (done).
************************************************************/
TcpServer::dissected TcpServer::dissect_msg_string(String^ undissected){
	dissected done;
	//Number of seats:
	done.seats = System::Convert::ToInt32(undissected->Substring(undissected->LastIndexOf(",")+1));
	undissected=undissected->Remove(undissected->LastIndexOf(","));
	//Under what name:
	done.who = undissected->Substring(undissected->LastIndexOf(",")+1);
	undissected=undissected->Remove(undissected->LastIndexOf(","));
	//For which sitting:
	done.sitting = System::Convert::ToInt32(undissected->Substring(undissected->LastIndexOf(",")+1));
	return done;
};

/*********************************************************
redisplayBookingsEverySixtySeconds: a thread that displays
in the server console window the booking information that
is refreshed every sixty seconds.
**********************************************************/
void TcpServer::redisplayBookingsEverySixtySeconds(){
	for(;;){
		for(int timeCounter = 4; timeCounter >= 1; timeCounter--){
			Console::WriteLine("Next booking report appears in "+System::Convert::ToString(timeCounter*15)+" seconds...");
			Thread::Sleep(15000);  //wait 15 seconds (15000ms)
		}
		Console::WriteLine("\nBookings as of "+DateTime::Now+":");
		christmaslunch->generateConsoleBookingReport();
	}
};

/**************************************************************
requestPlaces: takes the current stream writer as the argument
and sends the client the seats available via this stream writer
***************************************************************/
void TcpServer::requestPlaces(StreamWriter^ w){
	w->Write(this->christmaslunch->seatsAvailableMessage());
	w->Flush();
};

/************************************************************
makeBooking: a three step function, taking the current stream
writer and message string as arguments. The steps are -
Step 1: Create New A Booking Thread;
Step 2: Feedback on Processing Time -
	Step 2.1: Thread States;
	Step 2.2: Thread Timeout;
Step 3: Success.
**************************************************************/
void TcpServer::makeBooking(StreamWriter^ w, String^ m){
	/*---------------------------------
	Step 1: Create A New Booking Thread
	-----------------------------------*/
	/*create a delegate to the method that is the starting point of the thread (parameterized so an object can be passed to it):
	note - &Restaurant::addBooking is fully qualified - it generates a pointer to the object method name using & */
	ParameterizedThreadStart ^pts1 = gcnew ParameterizedThreadStart(this->christmaslunch,&Restaurant::addBooking);	
	//create the actual thread:
	Thread ^t1 = gcnew Thread(pts1);
	//dissect m (raw message from the client):
	dissected message = this->dissect_msg_string(m);
	//package dissected information into a new reservation structure:
	Reservation^ newres = gcnew Reservation(message.sitting,message.who,message.seats);
	//start the thread using the new reservation argument (newres):
	t1->Start(newres);
	/*-------------------------------------------------------------------------------------
	Step 2: Feedback on Processing Time (Step 2.1: Thread States; Step 2.2: Thread Timeout)
	---------------------------------------------------------------------------------------*/
	/*the following Booleans are used to prevent repeated thread state MESSAGES being displayed
	(i.e. they are used ONLY for user interface feedback): */
	bool waitsleepjoin, running, other, unsuccessful = false;
	DateTime dt1 = DateTime::Now;
	//continue feedback until Thread t1 has stopped:	
	while(t1->ThreadState!=ThreadState::Stopped){
		DateTime dt2 = DateTime::Now;
		/*---------------------
		Step 2.1: Thread States
		-----------------------*/
		//if the thread is blocked (by sleep or by lock) AND the feedback is yet displayed:
		if(t1->ThreadState==ThreadState::WaitSleepJoin&&!waitsleepjoin){
			waitsleepjoin=true;
			running=false;
			w->WriteLine("Waiting for reservation server...");
			w->Flush();
		}
		//thread is running:
		if(t1->ThreadState==ThreadState::Running&&!running){
			running=true;
			waitsleepjoin=false;
			w->WriteLine("Trying to book now...");
			w->Flush();
		}
		/*----------------------
		Step 2.2: Thread Timeout
		------------------------*/
		//determine milliseconds since beginning:
		Int64 t = (dt2.Ticks - dt1.Ticks)/10000;
		/*1000 milliseconds in a second therefore 10000ms = 10secs
		if thread has taken longer than 10 seconds AND (other) slow time message is yet displayed: */
		if(t>10000&&!other){
			other=true;
			running = waitsleepjoin = false;
			w->WriteLine("It is possible there were not enough seats available.");
			w->WriteLine("The process will continue for another 10 seconds.");
			w->Flush();
		};
		//TIMEOUT after 20 secs && this unsuccessful message is yet to be displayed:
		if(t>20000&&!unsuccessful){
			t1->Abort(newres);
			//(other) should already be true - and so no other messages appear - but just for safety:
			unsuccessful = running = waitsleepjoin = other = true;
			w->WriteLine("Sorry - your booking could not be confirmed.");
			w->WriteLine("Perhaps another booking came in before yours?");
			w->WriteLine("Please check the number of seats available and try again.");
			w->WriteLine("EOM");
			w->Flush();
		}
	}//the above is repeated while ThreadState of t1 is not Stopped
	/*-------------
	Step 3: Success
	---------------*/
	if(!unsuccessful){
		w->WriteLine("Accepted.  To cancel use your UNIQUE RESERVATION KEY: ");
		w->WriteLine("EXPECT_KEY");
		w->WriteLine(this->christmaslunch->feedback_reservation_key(newres));
		w->WriteLine("EOM");
		w->Flush();
	}
};

/****************************************************************************
cancelBooking: removes an old booking from the reservation storage structure
The author recommends future revision of this method - it currently does not
provide adequate feedback during the search and removal of the booking to the
client.  It also does not contain a provision for incorrect / non-existant
keys.
*****************************************************************************/
void TcpServer::cancelBooking(StreamWriter^ w, String^ m){
	// extract the reservation key from the request:
	String^ reservation_key = m->Substring(m->LastIndexOf(",")+1);
	// create the delegate:
	ParameterizedThreadStart ^pts2 = gcnew ParameterizedThreadStart(this->christmaslunch,&Restaurant::deleteBooking);	
	// create the thread:
	Thread ^t2 = gcnew Thread(pts2);
	// invoke the thread with the reservation key argument:
	t2->Start(reservation_key);
	/*until the thread has stopped (which could be better implemented
	in the future using the feedback system above for booking): */
	while(t2->ThreadState!=ThreadState::Stopped){}//a feedback system must be utilised {here} in future
	//currently no provision for non-existant keys - another mandatory future revision required:
	w->WriteLine("Complete. "+reservation_key->ToUpper()+" is gone.");
	w->Flush();
};

/****************************************************************************
ProcessThread: thread that handles the connection of the client, and 
*****************************************************************************/
void TcpServer::ProcessThread(Object^ clientObj)
{
	/*Provide a client connection for reservation service, and enable stream communication with this connection.
	Note in this instance, the TcpClient information is taken from the invoked thread argument - this enables multiple concurrency:*/
	TcpClient^ client = (TcpClient^) clientObj;
	//"Bind" a socket to the client IP End Point (the client's IPEndPoint is the combination of the client's IP Address and port):
	IPEndPoint^ clientEP = (IPEndPoint^)client->Client->RemoteEndPoint;
	//For testing using client address 127.0.0.1 - this is loopback IP address which will hit this server on same machine:
	Console::WriteLine("Connected on IP: {0} Port: {1}",clientEP->Address, clientEP->Port);
	//Create stream reader and writer from a client's Networkstream object to handle outgoing (writer) and incoming (reader) streams:
	StreamWriter^ writer = gcnew StreamWriter(client->GetStream());
	StreamReader^ reader = gcnew StreamReader(client->GetStream());
	//feedback using stream - let client know which port they are connected using:
	writer->WriteLine("Successful connection to the server on port {0}",clientEP->Port);
	writer->WriteLine(this->christmaslunch->welcomeMessage());
	writer->Flush();
	String^ msg;
	while (true) //while connected:
		{
			try //incase the connection is lost:
			{
				//get client request:
				msg = reader->ReadLine();
				reader->DiscardBufferedData();
				Console::WriteLine("Port [{0}] {1}",clientEP->Port, msg);

				//1 SECOND DELAY CORRESPONDING TO THE VALIDATION OF ALL REQUESTS:
				Thread::Sleep(1000);

				if(msg->Equals("DISCONNECT"))break; //Quit
				if(msg->IndexOf("1")==0)requestPlaces(writer); //Information (request 1)
				if(msg->IndexOf("2")==0)makeBooking(writer, msg); //Book (request 2)
				if(msg->IndexOf("3")==0)cancelBooking(writer, msg); //Cancel (request 3)
		    }
			catch(IOException^){break;} //connection is suddently lost, exit the connection loop.
		}//end of loop that continues while still connected
	//Mark the TcpClient instance as disposed, and request the socket to close the TCP connection:
	client->Close();
	Console::WriteLine("Connection to IP: {0} Port {1} closed.",clientEP->Address, clientEP->Port);
}