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