diff --git a/Automail/src/automail/Automail.java b/Automail/src/automail/Automail.java index 2eb9e1b07a8ab9d4885ee3eaf584cced9f8e07b0..fff050fe088a03f7b4734a1fe61e078169f5e828 100644 --- a/Automail/src/automail/Automail.java +++ b/Automail/src/automail/Automail.java @@ -1,20 +1,29 @@ package automail; +import com.unimelb.swen30006.wifimodem.WifiModem; import simulation.IMailDelivery; +import simulation.Simulation; public class Automail { public Robot[] robots; public MailPool mailPool; - public Automail(MailPool mailPool, IMailDelivery delivery, int numRobots) { - /** Initialize the MailPool */ - + public Automail(MailPool mailPool, IMailDelivery delivery, int numRobots, StatisticsLog statisticsLog, ChargeCalculator chargeCalculator ) { + + /** Initialize the MailPool */ this.mailPool = mailPool; /** Initialize robots */ robots = new Robot[numRobots]; - for (int i = 0; i < numRobots; i++) robots[i] = new Robot(delivery, mailPool, i); + for (int i = 0; i < numRobots; i++){ + robots[i] = new Robot(delivery, mailPool, i); + + // Each robots gets personal device to access statistics and charge calculator + RobotDevice newDevice = new RobotDevice(statisticsLog, chargeCalculator); + robots[i].device = newDevice; + + } } - + } diff --git a/Automail/src/automail/ChargeCalculator.java b/Automail/src/automail/ChargeCalculator.java new file mode 100644 index 0000000000000000000000000000000000000000..5ae2ff0df33e182940e9357c9b386f3c1b50c3ba --- /dev/null +++ b/Automail/src/automail/ChargeCalculator.java @@ -0,0 +1,41 @@ +package automail; + +import com.unimelb.swen30006.wifimodem.WifiModem; +import simulation.Building; +import simulation.Simulation; + +public class ChargeCalculator { + + public final WifiModem wifiModem; + private double activityUnitPrice; + private double markupPercentage; + + public ChargeCalculator(WifiModem wifiModem, double activityUnitPrice, double markupPercentage) { + this.wifiModem = wifiModem; + this.activityUnitPrice = activityUnitPrice; + this.markupPercentage = markupPercentage; + } + + + public double calculateCharge(MailItem item) { + int destination = item.destination_floor; + double activityUnits = ((2*(destination - Building.MAILROOM_LOCATION)) * Simulation.MOVEMENT_COST) + Simulation.LOOKUP_COST; + item.setActivityUnitsUtilized(activityUnits); + double activityCost = activityUnits * activityUnitPrice; + item.setActivityCost(activityCost); + double latestServiceFee = wifiModem.forwardCallToAPI_LookupPrice(destination); + double serviceFee = item.getServiceFee(); + if (latestServiceFee >= 0 ) { + item.setSuccessfulLookupCount(item.getSuccessfulLookupCount()+1); + serviceFee = latestServiceFee; + item.setServiceFee(latestServiceFee); + } + double charge = (activityCost + serviceFee) * (1 + markupPercentage); + item.setCharge(charge); + return charge; + } + + public double getActivityUnitPrice() { + return activityUnitPrice; + } +} diff --git a/Automail/src/automail/MailItem.java b/Automail/src/automail/MailItem.java index 2bdf9f2f8531b4494d96f326b8dd21cf4e3a2edc..493981ff46edd96242733f21d3cf6ee11dce5ecb 100644 --- a/Automail/src/automail/MailItem.java +++ b/Automail/src/automail/MailItem.java @@ -19,7 +19,13 @@ public class MailItem { /** The weight in grams of the mail item */ protected final int weight; + /* Holds the financial information associated with item's delivery */ private double serviceFee; + private int successfulLookupCount; + private double activityUnitsUtilized; + private double charge; + private double cost; + private double activityCost; /** * Constructor for a MailItem @@ -39,10 +45,17 @@ public class MailItem { } @Override + // The toString for the older version public String toString(){ return String.format("Mail Item:: ID: %6s | Arrival: %4d | Destination: %2d | Weight: %4d", id, arrival_time, destination_floor, weight); } + // The toString for the new version with the new statistics + public String toStringv2(){ + return String.format("Mail Item:: ID: %6s | Arrival: %4d | Destination: %2d | Weight: %4d | Charge %.2f " + + "| Cost: %.2f | Fee: %.2f | Activity: %.2f", id, arrival_time, destination_floor, weight, charge, + activityCost, serviceFee, activityUnitsUtilized); + } /** * * @return the destination floor of the mail item @@ -74,6 +87,10 @@ public class MailItem { public int getWeight(){ return weight; } + + public double getServiceFee() { + return serviceFee; + } static private int count = 0; static private Map<Integer, Integer> hashMap = new TreeMap<Integer, Integer>(); @@ -85,4 +102,44 @@ public class MailItem { if (hash == null) { hash = count++; hashMap.put(hash0, hash); } return hash; } + + public double getActivityUnitsUtilized() { + return activityUnitsUtilized; + } + + public void setActivityUnitsUtilized(double activityUnitsUtilized) { + this.activityUnitsUtilized = activityUnitsUtilized; + } + + public double getCharge() { + return charge; + } + + public void setCharge(double charge) { + this.charge = charge; + } + + public double getCost() { + return cost; + } + + public void setCost(double cost) { + this.cost = cost; + } + + public double getActivityCost() { + return activityCost; + } + + public void setActivityCost(double activityCost) { + this.activityCost = activityCost; + } + + public int getSuccessfulLookupCount() { + return successfulLookupCount; + } + + public void setSuccessfulLookupCount(int successfulLookupCount) { + this.successfulLookupCount = successfulLookupCount; + } } diff --git a/Automail/src/automail/MailPool.java b/Automail/src/automail/MailPool.java index 7977178aa28fece927ce960c9ab4011a86bc620b..9d3ce33a9aa315fd573a5a33aa5f1aae19d56b6d 100644 --- a/Automail/src/automail/MailPool.java +++ b/Automail/src/automail/MailPool.java @@ -14,6 +14,11 @@ import java.util.ListIterator; * */ public class MailPool { + + public MailItem getItem(int i) { + return pool.get(i).mailItem; + } + private class Item { int destination; double charge; @@ -59,15 +64,7 @@ public class MailPool { pool.sort(new ItemComparator()); } - public double calculateCharge(int itemIndex, double serviceFee, double activityUnitPrice, double markupPercentage){ - double robotsMovement = 5.0D; - double remoteLookup = 0.1D; - int destination = pool.get(itemIndex).destination; - double activityUnits = ((destination - Building.MAILROOM_LOCATION) * robotsMovement) + remoteLookup; - double activityCost = activityUnits * activityUnitPrice; - double charge = (activityCost + serviceFee) * (1 + markupPercentage); - return charge; - } + public void setItemCharge(int itemIndex, double charge){ Item item = new Item(pool.get(itemIndex).mailItem); @@ -107,6 +104,17 @@ public class MailPool { } } + public double calculateCharge(int itemIndex, double serviceFee, double activityUnitPrice, double markupPercentage){ + double robotsMovement = 5.0D; + double remoteLookup = 0.1D; + int destination = pool.get(itemIndex).destination; + double activityUnits = ((destination - Building.MAILROOM_LOCATION) * robotsMovement) + remoteLookup; + double activityCost = activityUnits * activityUnitPrice; + double charge = (activityCost + serviceFee) * (1 + markupPercentage); + return charge; + } + + /** * load up any waiting robots with mailItems, if any. */ diff --git a/Automail/src/automail/Robot.java b/Automail/src/automail/Robot.java index ff066165a2608fe944268beeae2596be77659d47..55ad972ac98f4908902512672a684b4ededb561b 100644 --- a/Automail/src/automail/Robot.java +++ b/Automail/src/automail/Robot.java @@ -5,6 +5,7 @@ import exceptions.ItemTooHeavyException; import simulation.Building; import simulation.Clock; import simulation.IMailDelivery; +import simulation.Simulation; /** * The robot delivers mail! @@ -13,8 +14,11 @@ public class Robot { static public final int INDIVIDUAL_MAX_WEIGHT = 2000; + /** Personal information in Robot class */ IMailDelivery delivery; + RobotDevice device; protected final String id; + /** Possible states the robot can be in */ public enum RobotState { DELIVERING, WAITING, RETURNING } public RobotState current_state; @@ -31,7 +35,6 @@ public class Robot { /** * Initiates the robot's location at the start to be at the mailroom * also set it to be waiting for mail. - * @param behaviour governs selection of mail items for delivery and behaviour on priority arrivals * @param delivery governs the final delivery * @param mailPool is the source of mail items */ @@ -87,8 +90,13 @@ public class Robot { break; case DELIVERING: if(current_floor == destination_floor){ // If already here drop off either way + /** Delivery complete, report this to the simulator! */ + double charge = device.calculateFinalCharge(deliveryItem); + device.recordDeliverySuccess(deliveryItem); delivery.deliver(deliveryItem); + + deliveryItem = null; deliveryCounter++; if(deliveryCounter > 2){ // Implies a simulation bug diff --git a/Automail/src/automail/RobotDevice.java b/Automail/src/automail/RobotDevice.java new file mode 100644 index 0000000000000000000000000000000000000000..21ac18e7b71501b27505b52982fb6390ee4f15e7 --- /dev/null +++ b/Automail/src/automail/RobotDevice.java @@ -0,0 +1,26 @@ +package automail; + +import com.unimelb.swen30006.wifimodem.WifiModem; +import simulation.Simulation; + +public class RobotDevice { + + public final StatisticsLog statisticsLog; + public final ChargeCalculator chargeCalculator; + + public RobotDevice(StatisticsLog statisticsLog, ChargeCalculator chargeCalculator) { + this.statisticsLog = statisticsLog; + this.chargeCalculator = chargeCalculator; + } + + public double calculateFinalCharge (MailItem item) { + double final_charge = chargeCalculator.calculateCharge(item) - Simulation.LOOKUP_COST; + return final_charge; + } + + public void recordDeliverySuccess(MailItem item) { + double activity_unit_price = chargeCalculator.getActivityUnitPrice(); + statisticsLog.recordDeliverySuccess(item, activity_unit_price); + } + +} diff --git a/Automail/src/automail/StatisticsLog.java b/Automail/src/automail/StatisticsLog.java new file mode 100644 index 0000000000000000000000000000000000000000..8ef7e45dfe9c4a35c741a180c3c0f8dd0de9dc38 --- /dev/null +++ b/Automail/src/automail/StatisticsLog.java @@ -0,0 +1,39 @@ +package automail; + +import simulation.Building; +import simulation.Simulation; + +public class StatisticsLog { + private int totalItemsDelivered; + private double totalBillableActivity; + private double totalActivityCost; + private double totalServiceCost; + private int numSuccessfulLookup; + private int numFailedLookup; + private int totalLookup; + + private final int ONE_LOOKUP = 1; + + + public void recordDeliverySuccess(MailItem deliveryItem, double activityUnitPrice) { + totalItemsDelivered++; + totalBillableActivity = totalBillableActivity + 2*(deliveryItem.getDestFloor() - Building.MAILROOM_LOCATION) + ONE_LOOKUP; + totalActivityCost = totalBillableActivity * activityUnitPrice; + totalServiceCost = totalServiceCost + deliveryItem.getServiceFee(); + numSuccessfulLookup = numSuccessfulLookup + deliveryItem.getSuccessfulLookupCount(); + numFailedLookup = numFailedLookup + (2 - deliveryItem.getSuccessfulLookupCount()); + totalLookup = numSuccessfulLookup+numFailedLookup; + } + + @Override + public String toString() { + return String.format("Total items delivered: %d \n" + + "Total billable activity: %.2f \n" + + "Total activity cost: %.2f \n" + + "Total service cost: %.2f \n" + + "Total failed lookups: %d \n" + + "Total successful lookups: %d \n" + + "Total lookups: %d",totalItemsDelivered, totalBillableActivity, totalActivityCost, totalServiceCost, + numFailedLookup, numSuccessfulLookup, totalLookup ); + } +} diff --git a/Automail/src/simulation/Simulation.java b/Automail/src/simulation/Simulation.java index 1dea0886e81f313ccc2fbc33f240cd85ade4425e..3fe8e05979e29faf89fb409bb33c1b673132d247 100644 --- a/Automail/src/simulation/Simulation.java +++ b/Automail/src/simulation/Simulation.java @@ -1,8 +1,6 @@ package simulation; -import automail.Automail; -import automail.MailItem; -import automail.MailPool; +import automail.*; import com.unimelb.swen30006.wifimodem.WifiModem; import exceptions.ExcessiveDeliveryException; import exceptions.ItemTooHeavyException; @@ -21,6 +19,11 @@ public class Simulation { private static boolean CHARGE_DISPLAY; private static double ACTIVITY_UNIT_PRICE; private static double MARKUP_PERCENTAGE; + public static final double MOVEMENT_COST = 5.0D; + public static final double LOOKUP_COST = 0.1D; + private static final boolean NEW = true; + private static final boolean OLD = false; + private static boolean version = OLD; /** Constant for the mail generator */ private static int MAIL_TO_CREATE; @@ -28,12 +31,20 @@ public class Simulation { private static ArrayList<MailItem> MAIL_DELIVERED; private static double total_delay = 0; + private static WifiModem wModem = null; + private static ChargeCalculator chargeCalculator = null; + private static StatisticsLog statisticsLog = null; + + /*---------------------------------------------------------------------------------------------------------------*/ public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException { /** Load properties for simulation based on either default or a properties file.**/ Properties automailProperties = setUpProperties(); + + /* Check which version to run based on the following condition */ + version = (CHARGE_DISPLAY && CHARGE_THRESHOLD != 0); //An array list to record mails that have been delivered MAIL_DELIVERED = new ArrayList<MailItem>(); @@ -42,7 +53,7 @@ public class Simulation { * If a program argument is entered, the first argument will be a random seed. * If not a random seed will be from a properties file. * Otherwise, no a random seed. */ - + /** Used to see whether a seed is initialized or not */ HashMap<Boolean, Integer> seedMap = new HashMap<>(); if (args.length == 0 ) { // No arg @@ -57,7 +68,8 @@ public class Simulation { } Integer seed = seedMap.get(true); System.out.println("#A Random Seed: " + (seed == null ? "null" : seed.toString())); - + + // Install the modem & turn on the modem try { System.out.println("Setting up Wifi Modem"); @@ -66,33 +78,35 @@ public class Simulation { } catch (Exception mException) { mException.printStackTrace(); } - /** - * This code section is for running a simulation - */ - /* Instantiate MailPool and Automail */ - MailPool mailPool = new MailPool(NUM_ROBOTS); - Automail automail = new Automail(mailPool, new ReportDelivery(), NUM_ROBOTS); + + /* Instantiate the main objects in the simulation */ + statisticsLog = new StatisticsLog(); + chargeCalculator = new ChargeCalculator(wModem, ACTIVITY_UNIT_PRICE, MARKUP_PERCENTAGE); + MailPool mailPool = new MailPool(NUM_ROBOTS); + Automail automail = new Automail(mailPool, new ReportDelivery(), NUM_ROBOTS, statisticsLog, chargeCalculator); MailGenerator mailGenerator = new MailGenerator(MAIL_TO_CREATE, MAIL_MAX_WEIGHT, mailPool, seedMap); - /** Generate all the mails */ + + /** Generate all the mail */ mailGenerator.generateAllMail(); + + /* START the simulation */ while(MAIL_DELIVERED.size() != mailGenerator.MAIL_TO_CREATE) { - // System.out.printf("Delivered: %4d; Created: %4d%n", MAIL_DELIVERED.size(), mailGenerator.MAIL_TO_CREATE); mailGenerator.addToMailPool(); - if(CHARGE_DISPLAY){ + if (version == NEW){ + // Calculate expected charge for each item int poolSize = mailPool.getPoolSize(); for(int i=0; i<poolSize; i++){ if(mailPool.isNewItem(i)) { - int destination = mailPool.getItemDestination(i); - double serviceFee = wModem.forwardCallToAPI_LookupPrice(destination); - double charge = mailPool.calculateCharge(i, serviceFee, ACTIVITY_UNIT_PRICE, MARKUP_PERCENTAGE); + double charge = chargeCalculator.calculateCharge(mailPool.getItem(i)); mailPool.setItemCharge(i, charge); mailPool.getItemInfo(i); } } - if(CHARGE_THRESHOLD != 0){ - mailPool.sortPriorityMailItems(CHARGE_THRESHOLD); - } - } + // Priority sort the items + mailPool.sortPriorityMailItems(CHARGE_THRESHOLD); + } // All items have been prioritized + + // Now send the Robots to deliver the mail try { automail.mailPool.loadItemsToRobot(); for (int i=0; i < NUM_ROBOTS; i++) { @@ -103,12 +117,16 @@ public class Simulation { System.out.println("Simulation unable to complete."); System.exit(0); } + // All robots out for delivery Clock.Tick(); } + // Simulation is finished. print results printResults(); System.out.println(wModem.Turnoff()); + // } - + /*---------------------------------------------------------------------------------------------------------------*/ + static private Properties setUpProperties() throws IOException { Properties automailProperties = new Properties(); // Default properties @@ -123,7 +141,7 @@ public class Simulation { // Read properties FileReader inStream = null; try { - inStream = new FileReader("automail.properties"); + inStream = new FileReader("swen30006-group-work-master/Automail/automail.properties"); automailProperties.load(inStream); } finally { if (inStream != null) { @@ -161,14 +179,21 @@ public class Simulation { System.out.println("Activity_Unit_Price: " + ACTIVITY_UNIT_PRICE); return automailProperties; } + /*---------------------------------------------------------------------------------------------------------------*/ static class ReportDelivery implements IMailDelivery { - + /** Confirm the delivery and calculate the total score */ public void deliver(MailItem deliveryItem){ if(!MAIL_DELIVERED.contains(deliveryItem)){ MAIL_DELIVERED.add(deliveryItem); - System.out.printf("T: %3d > Delivered(%4d) [%s]%n", Clock.Time(), MAIL_DELIVERED.size(), deliveryItem.toString()); + if (version == NEW) { + System.out.printf("T: %3d > Delivered(%4d) [%s]%n", Clock.Time(), MAIL_DELIVERED.size(), + deliveryItem.toStringv2()); + } else { + System.out.printf("T: %3d > Delivered(%4d) [%s]%n", Clock.Time(), MAIL_DELIVERED.size(), + deliveryItem.toString()); + } // Calculate delivery score total_delay += calculateDeliveryDelay(deliveryItem); } @@ -182,7 +207,9 @@ public class Simulation { } } - + + /*---------------------------------------------------------------------------------------------------------------*/ + private static double calculateDeliveryDelay(MailItem deliveryItem) { // Penalty for longer delivery times final double penalty = 1.2; @@ -191,9 +218,15 @@ public class Simulation { return Math.pow(Clock.Time() - deliveryItem.getArrivalTime(),penalty)*(1+Math.sqrt(priority_weight)); } + /*---------------------------------------------------------------------------------------------------------------*/ + public static void printResults(){ System.out.println("T: "+Clock.Time()+" | Simulation complete!"); System.out.println("Final Delivery time: "+Clock.Time()); System.out.printf("Delay: %.2f%n", total_delay); + if (version == NEW) System.out.println(statisticsLog.toString()); } + + /*---------------------------------------------------------------------------------------------------------------*/ + }