home *** CD-ROM | disk | FTP | other *** search
- package BANK is
- --------------------------------------------------------------------------
- --| BEGIN PROLOGUE
- --| DESCRIPTION : BANK defines a bank, the TELLER objects
- --| : within it, and the procedure PRINT_REPORT
- --| : (which reports on the status of the BANK).
- --| :
- --| : BANK is an abstract state machine, defining
- --| : a BANK object which contains TELLER objects.
- --| :
- --| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
- --| : object-oriented design and tasking
- --| : with Ada
- --| :
- --| LIMITATIONS : None
- --| AUTHOR(S) : Richard Conn (RLC)
- --| CHANGE LOG : 1/16/89 RLC Initial Design and Code
- --| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
- --| REMARKS : None
- --| PORTABILITY ISSUES : None
- --| END PROLOGUE
- --------------------------------------------------------------------------
-
- -- TRANSACTION requested of a TELLER
- type TRANSACTION is (ADD_NEW_CUSTOMER,
- GET_BALANCE,
- MAKE_DEPOSIT,
- MAKE_WITHDRAWAL);
-
- -- Unit of currency
- type DOLLAR is new FLOAT;
-
- -- Identification of a CUSTOMER
- type CUSTOMER_ID is new NATURAL range 1 .. NATURAL'LAST;
-
- -- Index of and Number of TELLER objects within the bank
- type TELLER_INDEX is new NATURAL range 1 .. 4;
-
- -- A TELLER_PERSON is an object to which a CUSTOMER may make a REQUEST
- -- The BANK tells the TELLER_PERSON to START_WORK, giving the
- -- TELLER_PERSON its TELLER_NUMBER
- task type TELLER_PERSON is
- entry REQUEST(ID : in out CUSTOMER_ID;
- KIND : in TRANSACTION;
- AMOUNT : in out DOLLAR);
- end TELLER_PERSON;
- -- These are the TELLER objects available at the bank
- TELLER : array(TELLER_INDEX) of TELLER_PERSON;
-
- -- PRINT_REPORT gives the transaction log of all the bank
- -- customers
- procedure PRINT_REPORT;
-
- -- STOP_WORK terminates all TELLER tasks
- procedure STOP_WORK;
- end BANK;
- --
- with CONSOLE;
- package body BANK is
- --------------------------------------------------------------------------
- --| BEGIN PROLOGUE
- --| DESCRIPTION : Package Body BANK implements the TELLER
- --| : tasks and the PRINT_REPORT procedure.
- --| : CUSTOMER data is maintained within the
- --| : BANK as a linked list.
- --| :
- --| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
- --| : object-oriented design and tasking
- --| : with Ada
- --| :
- --| LIMITATIONS : None
- --| AUTHOR(S) : Richard Conn (RLC)
- --| CHANGE LOG : 1/16/89 RLC Initial Design and Code
- --| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
- --| REMARKS : None
- --| PORTABILITY ISSUES : Uses CONSOLE (TEXT_IO), so is very portable;
- --| : no known portability problems.
- --| END PROLOGUE
- --------------------------------------------------------------------------
-
- -- Identifier of next customer
- NEXT_ID : CUSTOMER_ID := CUSTOMER_ID'FIRST;
-
- -- Linked list of customer data
- type CUSTOMER_DATA;
- type CUSTOMER_DATA_POINTER is access CUSTOMER_DATA;
- type CUSTOMER_DATA is record
- ID : CUSTOMER_ID;
- BALANCE : DOLLAR := 0.0;
- TRANSACTION_COUNT : NATURAL := 0;
- ATTEMPTED_OVERDRAWS : NATURAL := 0;
- NEXT : CUSTOMER_DATA_POINTER := null;
- end record;
-
- -- Count of number of transactions for each TELLER object
- TELLER_TRANSACTION_COUNT : array(TELLER_INDEX) of NATURAL
- := (others => 0);
-
- -- Pointers to first and last customer data entries
- FIRST_CUSTOMER : CUSTOMER_DATA_POINTER := null;
- LAST_CUSTOMER : CUSTOMER_DATA_POINTER := null;
-
- --
- -- package body BANK
- -- Print a report of the status of the BANK
- procedure PRINT_REPORT is
- CURRENT_CUSTOMER : CUSTOMER_DATA_POINTER;
- begin
-
- -- Check for any customers and issue an error message if none
- if NEXT_ID = CUSTOMER_ID'FIRST then
- CONSOLE.WRITE("The bank doesn't have any customers");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
-
- -- Generate report
- else
-
- -- Customer balance, transaction count, attempted overdraw
- -- count report
- CONSOLE.WRITE("**** BANK STATUS REPORT ****");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE(
- "Customer Balance Transactions Attempted_Overdraws");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CURRENT_CUSTOMER := FIRST_CUSTOMER;
- while CURRENT_CUSTOMER /= null loop
- CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.ID), 5);
- CONSOLE.WRITE(" ");
- CONSOLE.WRITE(FLOAT(CURRENT_CUSTOMER.BALANCE), 8, 2);
- CONSOLE.WRITE(" ");
- CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.TRANSACTION_COUNT), 8);
- CONSOLE.WRITE(" ");
- CONSOLE.WRITE(INTEGER(CURRENT_CUSTOMER.ATTEMPTED_OVERDRAWS),
- 8);
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
- end loop;
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
-
- -- Teller transaction count report
- CONSOLE.WRITE("Teller : ");
- for I in TELLER_INDEX loop
- CONSOLE.WRITE(INTEGER(I), 8);
- end loop;
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE("Transaction_Count: ");
- for I in TELLER_INDEX loop
- CONSOLE.WRITE(INTEGER(TELLER_TRANSACTION_COUNT(I)), 8);
- end loop;
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
-
- end if;
- end PRINT_REPORT;
-
- -- Terminate all TELLER tasks
- procedure STOP_WORK is
- begin
- for I in TELLER_INDEX loop
- abort TELLER(I);
- end loop;
- end STOP_WORK;
- --
- -- package body BANK
-
- -- FIND_CUSTOMER is used to find a CUSTOMER_DATA record
- -- based on a CUSTOMER_ID number
- function FIND_CUSTOMER(ID : in CUSTOMER_ID)
- return CUSTOMER_DATA_POINTER is
- CURRENT_CUSTOMER : CUSTOMER_DATA_POINTER;
- begin
- CURRENT_CUSTOMER := FIRST_CUSTOMER;
- while CURRENT_CUSTOMER /= null loop
- exit when CURRENT_CUSTOMER.ID = ID;
- CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
- end loop;
- return CURRENT_CUSTOMER;
- end FIND_CUSTOMER;
-
-
- --
- -- package body BANK
-
- task TELLER_ASSIGNER is
- -- This task assigns an ID number to a teller.
- -- TELLER_ASSIGNER is called by the TELLER_PERSON task when the
- -- TELLER_PERSON task first starts up.
- entry GET_TELLER_ID(ID : out TELLER_INDEX);
- end TELLER_ASSIGNER;
-
- task body TELLER_ASSIGNER is
- NEXT_TELLER_ID : TELLER_INDEX := TELLER_INDEX'FIRST;
- begin
- loop
- accept GET_TELLER_ID(ID : out TELLER_INDEX) do
- ID := NEXT_TELLER_ID;
- if NEXT_TELLER_ID /= TELLER_INDEX'LAST then
- NEXT_TELLER_ID := NEXT_TELLER_ID + 1;
- end if;
- end GET_TELLER_ID;
- end loop;
- end TELLER_ASSIGNER;
-
- --
- -- package body BANK
-
- -- Implementation of a TELLER task
- task body TELLER_PERSON is
-
- THIS_CUSTOMER : CUSTOMER_DATA_POINTER;
- MY_NUMBER : TELLER_INDEX;
-
- begin
-
- -- TELLER gets his ID number
- TELLER_ASSIGNER.GET_TELLER_ID(MY_NUMBER);
-
- -- TELLER loops on REQUESTs from CUSTOMERs
- loop
- accept REQUEST(ID : in out CUSTOMER_ID;
- KIND : in TRANSACTION;
- AMOUNT : in out DOLLAR) do
-
- -- Increment teller's transaction count
- TELLER_TRANSACTION_COUNT(MY_NUMBER)
- := TELLER_TRANSACTION_COUNT(MY_NUMBER) + 1;
-
- --
- -- package body BANK
-
- -- Process REQUEST
- case KIND is
-
- when ADD_NEW_CUSTOMER =>
- if LAST_CUSTOMER = null then
- LAST_CUSTOMER := new CUSTOMER_DATA;
- FIRST_CUSTOMER := LAST_CUSTOMER;
- else
- LAST_CUSTOMER.NEXT := new CUSTOMER_DATA;
- LAST_CUSTOMER := LAST_CUSTOMER.NEXT;
- end if;
- LAST_CUSTOMER.ID := NEXT_ID;
- ID := NEXT_ID;
- NEXT_ID := NEXT_ID + 1;
- THIS_CUSTOMER := LAST_CUSTOMER;
-
- when GET_BALANCE =>
- THIS_CUSTOMER := FIND_CUSTOMER(ID);
- AMOUNT := THIS_CUSTOMER.BALANCE;
-
- when MAKE_DEPOSIT =>
- THIS_CUSTOMER := FIND_CUSTOMER(ID);
- THIS_CUSTOMER.BALANCE
- := THIS_CUSTOMER.BALANCE + AMOUNT;
- AMOUNT := THIS_CUSTOMER.BALANCE;
-
- when MAKE_WITHDRAWAL =>
- THIS_CUSTOMER := FIND_CUSTOMER(ID);
- if THIS_CUSTOMER.BALANCE < AMOUNT then
- AMOUNT := 0.0;
- THIS_CUSTOMER.ATTEMPTED_OVERDRAWS :=
- THIS_CUSTOMER.ATTEMPTED_OVERDRAWS + 1;
- else
- THIS_CUSTOMER.BALANCE
- := THIS_CUSTOMER.BALANCE - AMOUNT;
- end if;
-
- end case;
-
- -- Increment CUSTOMER's transaction count
- THIS_CUSTOMER.TRANSACTION_COUNT
- := THIS_CUSTOMER.TRANSACTION_COUNT + 1;
-
- end REQUEST;
-
- end loop;
-
- end TELLER_PERSON;
-
- end BANK;
-
- --
- package CUSTOMER_WORLD is
- --------------------------------------------------------------------------
- --| BEGIN PROLOGUE
- --| DESCRIPTION : CUSTOMER_WORLD is an abstract state machine
- --| : which defines the collection of all CUSTOMERs
- --| : of the BANK. It allows the mainline procedure
- --| : to ADD a new CUSTOMER and TERMINATE_ALL
- --| : current CUSTOMERs. The CUSTOMER itself acts
- --| : as an independent task and requires no
- --| : direct interaction with the mainline once
- --| : it starts.
- --| :
- --| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
- --| : object-oriented design and tasking
- --| : with Ada
- --| :
- --| LIMITATIONS : None
- --| AUTHOR(S) : Richard Conn (RLC)
- --| CHANGE LOG : 1/16/89 RLC Initial Design and Code
- --| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
- --| REMARKS : None
- --| PORTABILITY ISSUES : None
- --| END PROLOGUE
- --------------------------------------------------------------------------
-
- -- Add another CUSTOMER task to the system
- procedure ADD;
-
- -- Terminate all CUSTOMERs in the system
- procedure TERMINATE_ALL;
-
- end CUSTOMER_WORLD;
-
- --
- with BANK;
- with RANDOM;
- package body CUSTOMER_WORLD is
-
- -- Support infix operations, like DOLLAR < DOLLAR
- use BANK;
-
- -- This is the amount that a CUSTOMER initially deposits
- -- into his account
- INITIAL_DEPOSIT : constant BANK.DOLLAR := 100.0;
-
- -- A CUSTOMER is a task, running concurrently with the TELLERs,
- -- other CUSTOMERs, and the mainline task
- task type CUSTOMER;
-
- -- CUSTOMER_POINTER is a pointer to a CUSTOMER, used to
- -- create new customers and track old customers in the linked
- -- list of customers used for task termination when the
- -- program is complete
- type CUSTOMER_POINTER is access CUSTOMER;
-
- -- These are the constructs used to create a linked list of
- -- the customers
- type CUSTOMER_LIST_ELEMENT;
- type CUSTOMER_LIST_ELEMENT_POINTER is access CUSTOMER_LIST_ELEMENT;
- type CUSTOMER_LIST_ELEMENT is record
- THIS_CUSTOMER : CUSTOMER_POINTER;
- NEXT : CUSTOMER_LIST_ELEMENT_POINTER := null;
- end record;
-
- -- Pointer to the first of the customers
- FIRST_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER := null;
- LAST_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER := null;
-
- --
- -- package body CUSTOMER_WORLD
-
- -- Implementation of the CUSTOMER task
- task body CUSTOMER is
-
- ID : BANK.CUSTOMER_ID := BANK.CUSTOMER_ID'FIRST;
- BALANCE : BANK.DOLLAR := 0.0;
- AMOUNT : BANK.DOLLAR := 0.0;
- MY_TRANSACTION : BANK.TRANSACTION;
-
- -- SELECT_TELLER makes a random selection of one of the
- -- BANK's tellers
- function SELECT_TELLER return BANK.TELLER_INDEX is
- begin
- return BANK.TELLER_INDEX(
- RANDOM.NUMBER * FLOAT(BANK.TELLER_INDEX'LAST) + 1.0);
- exception
- when others =>
- return BANK.TELLER_INDEX'LAST;
- end SELECT_TELLER;
-
- begin -- CUSTOMER activity
-
- -- As a new CUSTOMER, the first two transactions of the
- -- CUSTOMER are to ask the TELLER to add him as a new
- -- CUSTOMER and make an initial deposit
- BANK.TELLER(SELECT_TELLER).REQUEST(ID, BANK.ADD_NEW_CUSTOMER,
- BALANCE);
- BALANCE := INITIAL_DEPOSIT;
- BANK.TELLER(SELECT_TELLER).REQUEST(ID, BANK.MAKE_DEPOSIT, BALANCE);
-
- -- The rest of the life of the CUSTOMER is spent in the
- -- these steps within the following loop:
- -- (1) delay a random amount of time from 0 to 5 seconds
- -- (2) compute a random amount from -$50 to +$50
- -- (3) if the random amount is negative, attempt to
- -- withdraw that amount; if positive, deposit that
- -- amount
- loop
- delay DURATION(5.0 * RANDOM.NUMBER);
- AMOUNT := BANK.DOLLAR(100.0 * RANDOM.NUMBER - 50.0);
- if AMOUNT < DOLLAR'(0.0) then
- MY_TRANSACTION := BANK.MAKE_WITHDRAWAL;
- AMOUNT := -AMOUNT;
- else
- MY_TRANSACTION := BANK.MAKE_DEPOSIT;
- end if;
- BANK.TELLER(SELECT_TELLER).REQUEST(ID, MY_TRANSACTION, AMOUNT);
- end loop;
-
- end CUSTOMER;
-
- --
- -- package body CUSTOMER_WORLD
-
- -- Add a new CUSTOMER to the linked list
- procedure ADD is
- begin
-
- -- If the list is empty, start a new list
- if LAST_CUSTOMER = null then
- FIRST_CUSTOMER := new CUSTOMER_LIST_ELEMENT;
- LAST_CUSTOMER := FIRST_CUSTOMER;
-
- -- If the list is not empty, add an element onto it
- else
- LAST_CUSTOMER.NEXT := new CUSTOMER_LIST_ELEMENT;
- LAST_CUSTOMER := LAST_CUSTOMER.NEXT;
- end if;
-
- -- Start a new CUSTOMER task
- LAST_CUSTOMER.THIS_CUSTOMER := new CUSTOMER;
-
- end ADD;
-
- -- Terminate all CUSTOMER tasks by moving thru the linked list
- -- and explicitly terminating the tasks pointed to by the list
- -- elements.
- procedure TERMINATE_ALL is
- CURRENT_CUSTOMER : CUSTOMER_LIST_ELEMENT_POINTER;
- begin
- CURRENT_CUSTOMER := FIRST_CUSTOMER;
- while CURRENT_CUSTOMER /= null loop
- abort CURRENT_CUSTOMER.THIS_CUSTOMER.all;
- CURRENT_CUSTOMER := CURRENT_CUSTOMER.NEXT;
- end loop;
- end TERMINATE_ALL;
-
- end CUSTOMER_WORLD;
-
- --
- with BANK;
- with CUSTOMER_WORLD;
- with CONSOLE;
- procedure BD3 is -- BANK_DEMO_3
- --------------------------------------------------------------------------
- --| BEGIN PROLOGUE
- --| DESCRIPTION : Procedure BD3 (BANK DEMO 3) is a mainline
- --| : which demonstrates the operation of the
- --| : BANK. Upon invocation, the console becomes
- --| : a command processor for the bank manager.
- --| : The bank manager can obtain the status of
- --| : the bank (balances, number of transactions,
- --| : and attempted overdraws of all customers
- --| : and number of transactions processed by all
- --| : tellers) in a single or continuous (group
- --| : of 11 over about one minute) display.
- --| : The user at the console can also cause new
- --| : Customer tasks to be created and shut down
- --| : the system.
- --| :
- --| REQUIREMENTS SUPPORTED : Bank Demonstration Program to show
- --| : object-oriented design and tasking
- --| : with Ada
- --| :
- --| LIMITATIONS : None
- --| AUTHOR(S) : Richard Conn (RLC)
- --| CHANGE LOG : 1/16/89 RLC Initial Design and Code
- --| CHANGE LOG : 2/25/89 RLC Final Review Prior to Release
- --| REMARKS : None
- --| PORTABILITY ISSUES : Uses CONSOLE (TEXT_IO), so is very portable;
- --| : no known portability problems.
- --| END PROLOGUE
- --------------------------------------------------------------------------
-
- -- Line input from the console
- INPUT : CONSOLE.OUTSTRING;
-
- -- Number of continuous status reports displayed by the 'c'
- -- command before control is returned to the console
- NUMBER_OF_CONTINUOUS_STATUS_REPORTS : constant := 10;
-
- --
- -- procedure BD3
-
- begin -- mainline
-
- -- This is the beginning of the main loop. In this loop,
- -- a list of commands is printed on the console, the user
- -- at the console (as CUSTOMER 0) enters one of these commands
- -- followed by striking the RETURN key, and the command is
- -- processed.
- loop
-
- -- Command listing and prompt
- CONSOLE.WRITE("Enter");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE(" b for bank status");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE(" c for continuous bank status");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE(" s for start next customer");
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- CONSOLE.WRITE(" x for exit: ");
- CONSOLE.READ(INPUT);
-
- -- Interpretation and execution of input command
- case INPUT(1) is
- when 'b' | 'c' => -- Short or continuous bank status
- -- report
- BANK.PRINT_REPORT;
- if INPUT(1) = 'c' then
- for I in 1 .. NUMBER_OF_CONTINUOUS_STATUS_REPORTS loop
- delay 5.0;
- BANK.PRINT_REPORT;
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
- end loop;
- end if;
-
- when 's' => -- Start up a new CUSTOMER
- CUSTOMER_WORLD.ADD;
-
- when 'x' => -- Exit program
- CUSTOMER_WORLD.TERMINATE_ALL; -- Kill CUSTOMER tasks
- BANK.STOP_WORK; -- Kill TELLER tasks
- exit; -- Exit loop
-
- when ' ' => -- Non-error on a null input line
- null;
-
- when others => -- Other commands are invalid
- CONSOLE.WRITE("Invalid Command: ");
- CONSOLE.WRITE(CONSOLE.TRIM(INPUT));
- CONSOLE.WRITE(CONSOLE.NEW_LINE);
-
- end case;
-
- end loop;
-
- end BD3;
-