Previous Section  < Free Open Study >  Next Section

The Atm.cpp File


// Atm.cpp: The source file of the main classes composing the ATM 

// side of the application. It consists of all method and global

// data definitions required by these classes.



#include <stdio.h>

#include <iostream.h>

#include <string.h>

#include <math.h>



#include ''network.hpp''

#include ''atm.hpp''

#include ''trans.hpp''



// Definition of the two card slots in the ATM system, the

// card reader's slot, where a user inserts his or her card,

// and the ATM' s slot into which eaten cards are taken.

// These are simulated in this system by directories in the

// file system. The two path names are given to the ATM application

// as its first two command-line arguments.



char CardSlots[large_string];

char ATMSlots[large_string];



// The definition of the two format strings for simulating

// the ejecting and eating of bank cards. Can be changed to

// the equivalent Unix commands for portability.



const char* file_delete_format = ''del %s\\%s'';

const char* file_move_format = ''copy %s\\%s %s\\%s.%d'';





// The checking of an account name determines that it consists of

// numeric digits. (Note: The actual account on the Bank side of the

// application sees seven digits plus a terminating S or C for

// savings and checking, respectively).

int

bad_account(char* account)

{

     while (*account != '\0') {

         if (*account< '0' | | *account > '9') {

                    return(1);

         }

         account++;

     }

     return(0);

}

// For now PIN numbers and account numbers use the same algorithm.

// They may drift in the future.

int

bad_pin(char* pin)

{

     return(bad_account(pin));

}



// Each PhysicalCardReader has a name, which it uses as the name of

// the BankCardfile when it is inserted into CardSlot directory.

// This naming would not be necessary in a real system, since the

// hardware would take care of this naming problem. It appears in

// this application only for simplifying the simulation.

PhysicalCardReader::PhysicalCardReader(char* n)

{

     strcpy(name, n);

}



// The readinfo method tries to open a file in the CardSlots

// directory with the name of the card reader. This name

// would not be needed in a real system. The encoded data

// would be read off the card reader's hardware. The method

// returns a one if the card cannot be read, zero if it

// was read successfully. The buf argument is filled in

// with the first line of the file on success. It is assumed

// to contain a seven-digit numeric string (account number)

// followed by a four-digit numeric string (pin number).



int

PhysicalCardReader::readinfo(char* buf)

{

     FILE* fd;



     sprintf(buf, ''%s/%s'', CardSlots, name);

     if ((fd = fopen(buf, ''r'')) == NULL) {

          return(1);

     }

     fgets(buf, large_string, fd);

     fclose(fd);

     return(0);

}





// The simulation for eject cards is to remove the file from the card

// slot directory. In a real ATM system, this method would be a call

// to a hardware driver.

void PhysicalCardReader::eject_card()

{

      char buf[large_string];



      sprintf(buf, file_delete_format, CardSlots, name);

      system(buf);

}

// The simulation for eating cards is to move the BankCard file from

// the CardSlot directory to the ATM slot directory. In a real ATM

// system, this method would be a call to a hardware driver.

void

PhysicalCardReader::eat_card()

{

     char buf[large_string];

     static int count=1;



     sprintf(buf, file_move_format, CardSlots, name, ATMSlots,

name, count++);

     system(buf);

}



// The constructor for CardReader calls the constructor of its

//PhysicalCardReader.

CardReader::CardReader(char* name) : cardReader(name)

{

     validcard = 0;

     account[0] = pin[0] = '\0';

}





// The read_card method checks to see if there is a card in

// the slot. If there isn't, it returns 1. If there is and

// it isn't readable, then the card is rejected and a 1 is

// returned. If the data on the card is readable, then the account

// and PIN number are read from the card (account is assumed to be

// a seven-character numeric string, the PIN a four-digit numeric

// string). If the data cannot be parsed (the card is not a valid

// bank card), then a 1 is returned and the card is ejected.



int

CardReader::read_card()

{

     char buf[large_string];



     validcard = 0;

     switch (cardReader.readinfo(buf)) {

          case -1: // If the card couldn't be read, then eject it.



                      cardReader.eject_card();

                      return(1);

          case 1: // If there is no card, then report it to ATM.

                  return(1);

          case 0: // We have the information, parse it.

                  // If the account number is bad, return 1 and

                     eject.

                     sscanf(buf, ''%7s'', account);

                     account[7] = '\0';

                     if (bad_account(account)) {

                                cardReader.eject_card();

                                return(1);

                 }



                    //If the PIN number is bad, return 1 and

                      eject.

                    sscanf(&buf[7], ''%4s'', pin);

                    pin[4] = '\0';

                    if (bad_pin(pin)) {

                                cardReader.eject_card();

                                return(1);

                    }

          }

          validcard= 1;

          return(0);

}





// The accessor methods are required for verifying a user-

// supplied PIN number and building transactions. These are

// valid since there is a design situation facing policy between

// two separate key abstractions, i.e., the SuperKeypad and

// the CardReader. Both return 0 on success, 1 on failure.



int

CardReader::get_account(char* acc)

{

     if (validcard) {

          strcpy(acc, account);

     }

     return(!validcard);

}





int

CardReader::get_pin(char* p)

{

     if (validcard) {

          strcpy(p, pin);

     }

     return(!validcard);

}







// The following two methods simply delegate to their wrapped

// PhysicalCardReader class and execute its methods.

void

CardReader::eject_card()

{

     cardReader.eject_card();

}





void

CardReader::eat_card()

{

     cardReader.eat_card();

}







Keypad::Keypad()

{

     enabled = 0;

}



void

Keypad::enable()

{

     fflush(stdin);

     enabled =1;

}





void

Keypad::disable()

{

     enabled = 0;

}



// The getkey method reads a single character from the Keypad

// (in the simulation, the hardware is assumed to be the standard

// input). We assume the newline character to be the Enter key,

// implying that all input has been received. The method returns

// the character read on success, NULL terminator if the keypad

// is not enabled.

char

Keypad::getkey()

{

     return(enabled?getchar() : '\0');

}





void

DisplayScreen::display_msg(const char* msg)

{

     cout << ''@ATM Display@ '' << msg;

}





SuperKeypad::SuperKeypad()

{

     keypad = new Keypad;

     display = new DisplayScreen;

}



SuperKeypad::~SuperKeypad()

{

      delete keypad;

      delete display;

}



// This method delegates to its contained display screen. Such

// noncommunicating behavior is an argument for splitting the

// SuperKeypad class. However, the verify_pin()and

// get_transaction()methodsprovide more than enough cohesion

// of data to justify the SuperKeypad's existence.

void

SuperKeypad::display_msg(const char* msg)

{

     display->display_msg(msg);

}





// The verify_pin method enables the keypad, prompts the user

// for a PIN number, and checks it against the user-supplied

// PIN number. The method returns zero on success, nonzero

// on failure.

int

SuperKeypad::verify_pin(const char* pin_to_verify)

{

     char pin[small_string];

     int i = 0;



     keypad->enable();

     display->display_msg(''Enter Pin Number: '');

     while ((pin[i++] = keypad->getkey()) != EnterKey)

           ;

     pin[i]= '\0';

     keypad->disable();

     return(strncmp(pin,pin_to_verify,4));

}





// Note the case analysis on the type of transaction. This case

// analysis is necessary since our object-oriented design has

// bumped up against an action-oriented (text-menu driven) user

// interface as per our discussion in Chapter 9. At least this case

// analysis is restricted to one point in the design (one method)

// and hidden in the SuperKeypad class. Any classes higher in the

// system are oblivious to the case analysis.

Transaction*

SuperKeypad::get_transaction(char* account, char* pin)

{

     int i = 0;

     char amount_str[small_string], trans_type;

     char target_account[small_string];

     double amount;



     keypad->enable();

     do{

         display->display_msg(''Select a Transaction\n'');

         display->display_msg(''\tW)ithdrawal\n'');

         display->display_msg(''\tD)eposit\n'');

         display->display_msg(''\tB)alance\n'');

         display->display_msg(''\tT)ransfer\n'');

         display->display_msg(''\tQ)uit\n'');

         trans_type = keypad->getkey();

         while (keypad->getkey() != EnterKey)

                    ;

} while (trans_type != 'W' && trans_type != 'D' &&

         trans_type != 'B' && trans_type != 'T' && trans_type

         != 'Q');



if (trans_type == 'Q') {

     return(NULL);

}

display->display_msg(''Enter Account Type (S/C): '');

account[7] = keypad->getkey();

account[8] = '\0';

while (keypad->getkey() != EnterKey)

      ;



if (trans_type != 'B') {

     display->display_msg(''Enter Amount: '');

     while  ((amount_str[i++]  =  keypad->getkey())  !=

     EnterKey)

               ;

     amount_str[i-1] = '\0';

     amount = atof(amount_str);

}

if (trans_type == 'T') {

     display->display_msg(''Enter Target Account Number: '');

     i=0;

     while ((target_account[i++] = keypad->getkey()) !=

     EnterKey)

                ;

     target_account[i-1] = '\0';

     display->display_msg(''Enter Target Account Type (S/

C) : '');

     target_account[7] = keypad->getkey();

     target_account[8] = '\0';

     while (keypad->getkey() != EnterKey)

                ;

}

switch (trans_type) {

     case 'W' :

                 return(new Withdraw(account, pin, amount));

     case 'D':

                 return(new Deposit(account, pin, amount));

     case 'B':

                 return(new Balance(account, pin));

     case 'T':

                 return(new Transfer(account, pin,

                 target_account, amount));

         default:

                   cerr << ''Unknown type in get_transaction

                   switch statement\n'';

                   return(NULL);

     }

}





CashDispenser::CashDispenser(int initial_cash)

{

     cash_on_hand = initial_cash;

}





int

CashDispenser::enough_cash(int amount)

{

     return(amount <= cash_on_hand);

}



// We can give out only multiples of $10. The reader may want to

// elaborate on this class by giving it fixed numbers of $20 bills,

// $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps,

// theater tickets, etc., as well. Many warn the user that they are

// out of $10 bills and will dispense only multiples of $20. All of

// these items can be added to this class without impact on the rest

// of the system.

int

CashDispenser::dispense(int amount)

{

     amount -= amount % 10;

     if (enough_cash(amount)) {

          cout << ''@CashDispenser@ Giving the user '' << amount

<< '' cash\n'';

          return(0);

     }

     return(1);

}





int

DepositSlot::retrieve_envelope()

{

     cout << ''@DepositSlot@ Getting an envelope from the

user\n'';

     return(0);

}





// The receipt printer simulates the printing of receipts by

// creating a Receipts file in the current working directory.

// Again, the reader can elaborate on this class, adding a number

// of error checks, paper availability, etc. Like the cash

// dispenser, this is left as an exercise // to the reader since it

// adds no pedagogical benefit to this example.

void

ReceiptPrinter::print(TransactionList* translist)

{

   FILE* fd;



   cout << ''@ReceiptPrinter@ Your receipt is as follows: \n'';

   if ((fd = fopen(''receipt'', ''w'')) == NULL) {

      fd = stdout;

   }

      translist->print(fd);

   if (fd != stdout) {

       fclose(fd);

   }

}







// The BankProxy is an extremely important class. It is the

// representative of the Bank class within the ATM application. It

// is merely a wrapper for the Network class, which is a wrapper

// itself for which transport mechanism a distributed process is

// going to use for communication. In this example, I chose to

// simulate the network, but readers are free to use any network

// or byte-transfer mechanism they wish. The application is

// completely independent of this mechanism. (Note: Changes in the

// byte-transfer mechanism affect only the Network class's

// implementation.)

BankProxy::BankProxy(Network* n)

{

   network = n;

}





// When a BankProxy needs to process a transaction, it asks its

// Network object to send it. Assuming the send works correctly,

// the method then asks the Network for a response, which takes the

// form of a status integer (0,1, indicating success or failure on

// part of the real Bank class living in the bank's application

// space). If other Transaction application-specific data is

// required, then it is sent to the appropriate transaction's

// update message. This is to allow the Bank to update the state of

// a transaction in the ATM's application space from changes

// generated from the Bank's application space. Currently, only

// the Balance derived transaction uses this method to update its

// balance from the account in the Bank's application space.

int

BankProxy::process(Transaction* t)

{

   int status, count;

   char other_info[small_string];

   if (network-> send(t)) {

      return(1);

   }

   count = network->receive(status, other_info);

   if (count) {

     t->update(other_info, count);

   }

     return(status);

}





// Anew ATM object is given its Bank Proxy, a name to be handed down

// to its PhysicalCardReader (only needed for simulation), and its

// initial cash.

ATM::ATM(BankProxy* b, char* name, int cash)

{

     bank = new BankProxy(*b);

     cardReader = new CardReader(name);

     superKeypad = new SuperKeypad;

     cashDispenser = new CashDispenser(cash);

     depositSlot = new DepositSlot;

     receiptPrinter = new ReceiptPrinter;

     translist = new TransactionList(max_transaction_atm);

}





ATM::~ATM()

{

     delete bank;

     delete cardReader;

     delete superKeypad;

     delete cashDispenser;

     delete depositSlot;

     delete receiptPrinter;

     delete translist;

}



// The activate method for the ATM class is the main driver for the

// ATM objects. This method puts up the welcome message and waits

// for a card to become available (in simulation, a card becomes

// available when a user copies a file with the PhysicalCard-

// Reader's name into the CardSlots directory). When a card is

// available, the ATM retrieves the account and PIN number from the

// CardReader. It then asks its SuperKeypad to verify the PIN.

// The SuperKeypad verifies the PIN by getting a PIN number from the

// user and ensuring that it equals the one from the card. The

// actual check will be done by the Bank, which ensures that the PIN

// is equal to the one stored in the Account object.

// Once the PIN is verified, this method asks the SuperKeypad to

// collect and build a transaction. It preprocesses the

// transaction (handling things like getting envelopes for

// deposits, checking the cash dispenser to ensure enough cash is

// available for a withdrawal, etc.). If preprocessing was

// successful, it then asks its BankProxy to process the

// transaction. This method packages up the transaction, ships it

// over the network to the Bank's application, and collects a

// response from the Bank's application via the network. Notice

// the transparent nature of the interprocess communication. At

// design time we were able to completely ignore this distributed

// processing. Assuming the processing went well, we then execute

// a postprocess, which performs tasks like giving the user his or

// her money, etc. This method repeats the processing of the

// transaction until the user selects Quit, which requires the

// SuperKeypad::get_transaction method to return NULL. At this

// time the receipt printer generates a receipt and ejects the

// card.

void

ATM::activate()

{

     char account[small_string], pin[small_string];

     int count = 1, verified;

     Transaction* trans;



     while (1) {

          superKeypad->display_msg(''Welcome to the Bank of

Heuristics!!!\n'');

          superKeypad->display_msg(''Please Insert Your Card In

the Card Reader\n'');

// Get a card.

           while (cardReader->read_card()!=0) {

                      ;

           }

           cardReader->get_account(account);

           cardReader->get_pin(pin);

// Try three times to verify the PIN number.

          do {

                      verified = superKeypad->verify_pin(pin);

          } while (verified != 0 && count++ < 3);

// If it couldn't be verified, then eat the card.

           if (verified != 0) {

                       superKeypad->display_msg(''Sorry,  three

strikes and you're out!!! \n'');

                       cardReader->eat_card();

           }

           else {

// Otherwise, keep getting Transactions until the user asks to

// quit.

// while ((trans = superKeypad->get_transaction(account, pin))

// != NULL) {

// Preprocess the transaction, if necessary. The default is to do

// nothing.

// if (trans->preprocess(this) == 0) {

// If preprocessing was successful, then process the Transaction.

// If the Bank says the Transaction is valid, then add it to the

// current list (for the receipt) and carry out any postprocessing.

// if (bank->process(trans) == 0)

// { translist->add_trans(trans);

// trans->postprocess(this);

              }

// If problems occur, display an appropriate message and continue.

              else {

                 superKeypad->display_msg(''The Bank Refuses Your

Transaction!\n'');

                 superKeypad->display_msg(''Contact  your  Bank

Representative.\n'');

                 delete trans;

              }

                              }

                              else {

              superKeypad->display_msg(''This ATM is unable to

              comply with your'');

              superKeypad->display_msg('' request at this

              time.\n'');

delete trans;

                                }

                     }

//When we're done, print the receipt, clean up the Transaction

// list, and eject the card. We're now ready to loop for another

// user.

                     receiptPrinter->print(translist);

                     translist->cleanup();

                     cardReader->eject_card();

              }

      }

}





// These are methods used by derived types of Transaction,

// specifically, in their pre-/post-process methods.

int

ATM::retrieve_envelope()

{

     return(depositSlot->retrieve_envelope());

}



int

ATM::enough_cash(double amount)

{

     return(cashDispenser->enough_cash((int) amount));

}





int

ATM::dispense_cash(double amount)

{

     return(cashDispenser->dispense((int) amount));

}

    Previous Section  < Free Open Study >  Next Section