0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 1997-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026:
0027: package org.cougaar.logistics.plugin.inventory;
0028:
0029: import org.cougaar.core.service.LoggingService;
0030: import org.cougaar.glm.ldm.asset.Inventory;
0031: import org.cougaar.glm.ldm.asset.NewScheduledContentPG;
0032: import org.cougaar.glm.ldm.asset.SupplyClassPG;
0033: import org.cougaar.glm.ldm.GLMFactory;
0034: import org.cougaar.glm.ldm.plan.AlpineAspectType;
0035: import org.cougaar.glm.ldm.plan.PlanScheduleType;
0036: import org.cougaar.glm.ldm.plan.QuantityScheduleElement;
0037: import org.cougaar.logistics.ldm.Constants;
0038: import org.cougaar.logistics.plugin.inventory.TimeUtils;
0039: import org.cougaar.logistics.plugin.utils.ScheduleUtils;
0040: import org.cougaar.logistics.servlet.LogisticsInventoryServlet;
0041: import org.cougaar.planning.ldm.asset.PGDelegate;
0042: import org.cougaar.planning.ldm.asset.PropertyGroup;
0043: import org.cougaar.planning.ldm.measure.Duration;
0044: import org.cougaar.planning.ldm.measure.Rate;
0045: import org.cougaar.planning.ldm.measure.Scalar;
0046: import org.cougaar.planning.ldm.plan.*;
0047: import org.cougaar.planning.ldm.plan.AspectType;
0048: import org.cougaar.planning.plugin.util.PluginHelper;
0049: import org.cougaar.util.UnaryPredicate;
0050:
0051: import java.util.*;
0052:
0053: /** The LogisticsInventoryBG is responsible for maintaining all
0054: * information concerning an Inventory Asset. The Logistics
0055: * InventoryBG collects Withdraws, ProjectWithdraws, Supply and
0056: * ProjectSupply tasks as well as calculated information such as
0057: * inventory levels, refill levels and demand distribution over
0058: * time.
0059: **/
0060: public class LogisticsInventoryBG implements PGDelegate {
0061:
0062: // Bucket will be a knob, this implementation temporary
0063: private long msec_per_bucket = TimeUtils.MSEC_PER_DAY * 1;
0064:
0065: protected LogisticsInventoryPG myPG;
0066: protected transient long startCDay;
0067: protected long arrivalTime;
0068: protected long startTime;
0069: protected long oplanEndTime;
0070: protected int timeZero;
0071: private transient LoggingService logger;
0072: private transient LogisticsInventoryLogger csvLogger = null;
0073: private transient LogisticsInventoryFormatter csvWriter = null;
0074: // customerHash holds the time(long) of the last actual is seen
0075: // for each customer
0076: private Map customerHash;
0077: private TaskUtils taskUtils;
0078:
0079: protected int endOfLevelSixBucket;
0080: protected List refillProjections;
0081: protected List overlappingRefillProjections;
0082: protected List refillRequisitions;
0083: protected List dueOutList;
0084: // projectedDemandList mirrors dueOutList, each element
0085: // contains the sum of all Projected Demand for corresponding
0086: // bucket in dueOutList
0087: protected double projectedDemandArray[];
0088: protected double criticalLevelsArray[];
0089: protected double inventoryLevelsArray[];
0090: // Policy inputs
0091: protected long bucketSize = msec_per_bucket;
0092: protected int criticalLevel = 3; // num buckets
0093: protected int reorderPeriod = 3; // num buckets
0094: protected int ost = 1;
0095:
0096: // Lists for csv logging & UI support
0097: protected List projWithdrawList;
0098: protected List withdrawList;
0099: protected List projSupplyList;
0100: protected List supplyList;
0101: protected List targetLevelsList;
0102: protected Schedule bufferedTargetLevels;
0103: protected List actualDemandTasksList;
0104: protected Schedule bufferedCriticalLevels;
0105: protected Schedule bufferedInventoryLevels;
0106: // booleans used for recalculations
0107: private boolean failures = false;
0108: private boolean compute_critical_levels = true;
0109: private boolean regenerate_projected_demand = false;
0110: private boolean recalculate_initial_level = true;
0111: protected int deletionBucket = -1;
0112:
0113: public LogisticsInventoryBG(LogisticsInventoryPG pg) {
0114: myPG = pg;
0115: customerHash = new HashMap();
0116: dueOutList = new ArrayList();
0117: refillProjections = new ArrayList();
0118: refillRequisitions = new ArrayList();
0119: overlappingRefillProjections = new ArrayList();
0120: projectedDemandArray = new double[180];
0121: criticalLevelsArray = new double[180];
0122: inventoryLevelsArray = new double[180];
0123: Arrays.fill(criticalLevelsArray, Double.NEGATIVE_INFINITY);
0124: projWithdrawList = new ArrayList();
0125: withdrawList = new ArrayList();
0126: projSupplyList = new ArrayList();
0127: supplyList = new ArrayList();
0128: targetLevelsList = new ArrayList();
0129: actualDemandTasksList = new ArrayList();
0130: endOfLevelSixBucket = 1;
0131: }
0132:
0133: public void initialize(long startTime, long oplanEndTime,
0134: int criticalLevel, int reorderPeriod, int ost,
0135: long bucketSize, long now, boolean logToCSV,
0136: InventoryManager parentPlugin) {
0137: this .startTime = startTime;
0138: // Set initial level of inventory from time zero to today. This assumes that the inventory
0139: // is created because the existence of demand and the RefillGenerator will be run
0140: // on this inventory before time has advanced, thus the RefillGenerator will set
0141: // the inventory level for today and in general we always assume that levels prior
0142: // to today have been set before the RefillGenerator is run
0143: // Contract: the inventory for yesterday is always valid because initially it is
0144: // set by the behavior group of the inventory.
0145: // FSC - HOURLY:
0146: this .bucketSize = bucketSize;
0147: msec_per_bucket = this .bucketSize;
0148: timeZero = (int) ((startTime / msec_per_bucket) - 1);
0149:
0150: //Initialize with initial level since the refill generator won't start setting inv levels
0151: //until the first day it processes which is today + OST - so depending on OST it could be a while.
0152: inventoryLevelsArray[0] = myPG.getInitialLevel();
0153:
0154: logger = parentPlugin.getLoggingService(this );
0155: if (logToCSV) {
0156: csvLogger = LogisticsInventoryLogger.createInventoryLogger(
0157: myPG.getResource(), myPG.getOrg(), false,
0158: parentPlugin);
0159: csvWriter = new LogisticsInventoryFormatter(csvLogger,
0160: new Date(startCDay), parentPlugin);
0161: }
0162: taskUtils = parentPlugin.getTaskUtils();
0163: if (logger.isDebugEnabled()) {
0164: logger.debug("Start day: "
0165: + TimeUtils.dateString(startTime) + ", time zero "
0166: + TimeUtils.dateString(timeZero));
0167: }
0168: this .oplanEndTime = oplanEndTime;
0169: this .criticalLevel = criticalLevel;
0170: this .reorderPeriod = reorderPeriod;
0171: this .ost = ost;
0172: bufferedCriticalLevels = ScheduleUtils
0173: .buildSimpleQuantitySchedule(0, startTime, startTime
0174: + (TimeUtils.MSEC_PER_DAY * 10));
0175: bufferedTargetLevels = ScheduleUtils
0176: .buildSimpleQuantitySchedule(0, startTime, startTime
0177: + (TimeUtils.MSEC_PER_DAY * 10));
0178: bufferedInventoryLevels = ScheduleUtils
0179: .buildSimpleQuantitySchedule(myPG.getInitialLevel(),
0180: startTime, startTime
0181: + (TimeUtils.MSEC_PER_DAY * 10));
0182: }
0183:
0184: //Call reinitialize which does the default that has to be done
0185: //on set up after rehydration. It does the minimum initialization
0186: //that has to be done, even upon rehydration.
0187: //which does the initial set up when the inventory is created.
0188: public void reinitialize(boolean logToCSV,
0189: UtilsProvider parentPlugin) {
0190: logger = parentPlugin.getLoggingService(this );
0191: if (logToCSV) {
0192: csvLogger = LogisticsInventoryLogger.createInventoryLogger(
0193: myPG.getResource(), myPG.getOrg(), true,
0194: parentPlugin);
0195: csvWriter = new LogisticsInventoryFormatter(csvLogger,
0196: new Date(startCDay), parentPlugin);
0197: }
0198: taskUtils = parentPlugin.getTaskUtils();
0199: }
0200:
0201: public void addWithdrawProjection(Task task) {
0202: if (!task.getVerb().equals(Constants.Verb.PROJECTWITHDRAW)) { // assertion/pre-condition
0203: Exception exception = new Exception(
0204: "Adding non-PROJECTWITHDRAW task to inventory BG");
0205: if (logger.isErrorEnabled()) {
0206: logger
0207: .error(
0208: ".addWithdrawProjection - adding non-PROJECTWITHDRAW task "
0209: + task + " to inventory BG.",
0210: exception);
0211: }
0212: }
0213:
0214: long start = taskUtils.getStartTime(task);
0215: long end = taskUtils.getEndTime(task);
0216: // THIS ONE
0217: int bucket_start = convertTimeToBucket(start, false);
0218: int bucket_end = convertTimeToBucket(end, true);
0219: // Adding projections mean changed critical levels and
0220: // target levels. Set boolean to recompute critical
0221: // levels and clear targetLevelsList for CSV logging
0222: compute_critical_levels = true;
0223:
0224: // StartBucket for the inventory is the first bucket which has not seen any deletions
0225: // It is possible to have a case where a projection is added to buckets in the past
0226: // because the allocation results on a projection can change until the end of the
0227: // task which may still be in the future. Allow projections to be added to buckets
0228: // in the past but not to buckets before the start bucket of the inventory.
0229: if (bucket_start < getStartBucket()) {
0230: if (bucket_end < getStartBucket()) {
0231: if (logger.isErrorEnabled()) {
0232: logger
0233: .error("addWithdrawProjection not adding old projection. startBucket is "
0234: + TimeUtils
0235: .dateString(convertBucketToTime(getStartBucket()))
0236: + ", task "
0237: + taskUtils.taskDesc(task));
0238:
0239: }
0240: return;
0241: } else {
0242: if (logger.isInfoEnabled()) {
0243: logger
0244: .info("addWithdrawProjection not adding projection to deleted buckets. "
0245: + "startBucket is "
0246: + TimeUtils
0247: .dateString(convertBucketToTime(getStartBucket()))
0248: + ", task "
0249: + taskUtils.taskDesc(task));
0250: }
0251: bucket_start = getStartBucket();
0252: }
0253: }
0254:
0255: //If this new projection effects the initial level demand then recalc.
0256: if ((bucket_start <= getInitialLevelDemandEndBucket())
0257: && (bucket_end >= convertTimeToBucket(arrivalTime,
0258: false))) {
0259: recalculate_initial_level = true;
0260: }
0261:
0262: if (bucket_end >= projectedDemandArray.length) {
0263: projectedDemandArray = expandArray(projectedDemandArray,
0264: bucket_end);
0265: }
0266: while (bucket_end >= dueOutList.size()) {
0267: dueOutList.add(new ArrayList());
0268: }
0269: for (int i = bucket_start; i < bucket_end; i++) {
0270: List list = (List) dueOutList.get(i);
0271: list.add(task);
0272: updateProjectedDemandList(task, i, start, end, true);
0273: }
0274: }
0275:
0276: public void addWithdrawRequisition(Task task) {
0277: if (!task.getVerb().equals(Constants.Verb.WITHDRAW)) { // assertion/pre-condition
0278: Exception exception = new Exception(
0279: "Adding non-WITHDRAW task to inventory BG");
0280: logger.error(
0281: ".addWithdrawRequisition - adding non-WITHDRAW task "
0282: + task + " to inventory BG.", exception);
0283: }
0284:
0285: long endTime = taskUtils.getEndTime(task);
0286: int bucket = convertTimeToBucket(endTime, false);
0287: if (bucket < getStartBucket()) {
0288: if (logger.isErrorEnabled()) {
0289: logger
0290: .error("addWithdrawRequisition not adding old requisition. startBucket is "
0291: + TimeUtils
0292: .dateString(convertBucketToTime(getStartBucket()))
0293: + ", task " + taskUtils.taskDesc(task));
0294: }
0295: return;
0296: }
0297: Object org = TaskUtils.getCustomer(task);
0298: if (org != null) {
0299: Long lastActualSeen = (Long) customerHash.get(org);
0300: if ((lastActualSeen == null)
0301: || (endTime > lastActualSeen.longValue())) {
0302: customerHash.put(org, new Long(endTime));
0303: }
0304: }
0305: while (bucket >= dueOutList.size()) {
0306: dueOutList.add(new ArrayList());
0307: }
0308: List list = (List) dueOutList.get(bucket);
0309: list.add(task);
0310: }
0311:
0312: private void regenerateProjectedDemandList() {
0313: // clear out old demand
0314: // Arrays.fill(projectedDemandArray, 0.0);
0315: Arrays.fill(projectedDemandArray, getStartBucket(),
0316: projectedDemandArray.length - 1, 0.0);
0317: int size = dueOutList.size();
0318: for (int i = 0; i < size; i++) {
0319: Collection list = getProjectedDemandTasks(i);
0320: Iterator list_itr = list.iterator();
0321: while (list_itr.hasNext()) {
0322: Task task = (Task) list_itr.next();
0323: long start = taskUtils.getStartTime(task);
0324: long end = taskUtils.getEndTime(task);
0325: updateProjectedDemandList(task, i, start, end, true);
0326: }
0327: }
0328: }
0329:
0330: // ******* HERE LIES A BUG
0331: // Boundary condition bug - daily projected demands do not
0332: // jive when comparing daily buckets to 3-day buckets
0333: // Updates the running demand sum per bucket
0334: // AHF 7/31/02 - The bug actually is in the adding of the projection for multiple day buckets
0335: // see bug #1823
0336: private void updateProjectedDemandList(Task task, int bucket,
0337: long start, long end, boolean add) {
0338: if (bucket < 0) {
0339: logger.error("updateProjectedDemandList got bucket "
0340: + bucket + " for task " + task, new Throwable());
0341: bucket = 0;
0342: }
0343: if (bucket < getStartBucket()) {
0344: if (logger.isInfoEnabled()) {
0345: logger
0346: .info("updateProjectedDemandList not adding demand for old projection. startBucket is "
0347: + TimeUtils
0348: .dateString(convertBucketToTime(getStartBucket()))
0349: + ", task " + taskUtils.taskDesc(task));
0350: }
0351: return;
0352: }
0353: double demand = getProjectionTaskDemand(task, bucket, start,
0354: end);
0355: if (add) {
0356: if (bucket >= projectedDemandArray.length) {
0357: projectedDemandArray = expandArray(
0358: projectedDemandArray, bucket);
0359: }
0360: projectedDemandArray[bucket] += demand;
0361: } else {
0362: projectedDemandArray[bucket] -= demand;
0363: }
0364: // logger.error("Bucket "+bucket+": new demand "+demand+", total is "+
0365: // projectedDemandArray[bucket]);
0366: }
0367:
0368: public double getProjectionTaskDemand(Task task, int bucket,
0369: long start, long end) {
0370: // get the time spanned (if any) for this task within the specified bucket
0371: long bucket_start = convertBucketToTime(bucket);
0372: long bucket_end = bucket_start + msec_per_bucket;
0373: long interval_start = Math.max(start, bucket_start);
0374: long interval_end = Math.min(end, bucket_end);
0375: long time_spanned = interval_end - interval_start;
0376: if (time_spanned <= 0) {
0377: // this bucket does not fall into the task's start..end timespan, so
0378: // there is no demand for this bucket
0379: return 0.0;
0380: } else if (time_spanned > bucketSize) {
0381: logger.error("time_spanned " + time_spanned
0382: + " exceeds bucketSize " + bucketSize
0383: + " for task " + task.getUID());
0384: return 0.0;
0385: }
0386: // return the demand within the overlap interval
0387: Rate rate = taskUtils.getRate(task, interval_start,
0388: interval_end);
0389: Duration d = Duration.newMilliseconds((double) time_spanned);
0390: Scalar scalar = (Scalar) rate.computeNumerator(d);
0391: return taskUtils.getDouble(scalar);
0392: }
0393:
0394: public void removeWithdrawRequisition(Task task) {
0395: if (task.isDeleted()) {
0396: int end_bucket = convertTimeToBucket(taskUtils
0397: .getReportedEndTime(task), false);
0398: if (logger.isDebugEnabled()) {
0399: logger.debug("Removing task "
0400: + taskUtils.taskDesc(task));
0401: }
0402: setDeletionBucket(end_bucket);
0403: }
0404: for (int i = 0; i < dueOutList.size(); i++) {
0405: List list = (List) dueOutList.get(i);
0406: if (list != null) {
0407: list.remove(task);
0408: }
0409: }
0410: }
0411:
0412: /** Called by SupplyExpander to remove a Withdraw task that has either
0413: * been rescinded or changed.
0414: * @param task The ProjectWithdraw task being removed
0415: **/
0416: // TODO possible gotcha - only removing the deleted task from reported end time back.
0417: // could it be hiding somewhere else in the list - TEST
0418: public void removeWithdrawProjection(Task task) {
0419: if (task.isDeleted()) {
0420: int end_bucket = convertTimeToBucket(taskUtils
0421: .getReportedEndTime(task), true);
0422: if (logger.isDebugEnabled()) {
0423: logger.debug("Removing task "
0424: + taskUtils.taskDesc(task));
0425: }
0426: setDeletionBucket(end_bucket - 1);
0427: } else {
0428: compute_critical_levels = true;
0429: recalculate_initial_level = true;
0430: regenerate_projected_demand = true;
0431: }
0432: List list;
0433: for (int i = 0; i < dueOutList.size(); i++) {
0434: list = (List) dueOutList.get(i);
0435: if (list != null) {
0436: list.remove(task);
0437: }
0438: }
0439: }
0440:
0441: /** Called by all methods which remove tasks from lists.
0442: * When a task is deleted, the last affected bucket back to the beginning of
0443: * list are emptied IFF the buckets have not already been emptied.
0444: * @param bucket Last effected bucket by task deletion
0445: **/
0446: protected void setDeletionBucket(int bucket) {
0447: if (bucket > deletionBucket) {
0448: deletionBucket = bucket;
0449: }
0450: }
0451:
0452: public void addRefillRequisition(Task task) {
0453: boolean taskAdded = false;
0454:
0455: PlanElement pe = task.getPlanElement();
0456: if (pe != null) {
0457: AllocationResult ar = pe.getReportedResult();
0458: if (ar == null) {
0459: ar = pe.getEstimatedResult();
0460: }
0461: if (ar != null) {
0462: if (!ar.isPhased()) {
0463: long endTime = taskUtils.getReportedEndTime(task);
0464: addTaskToBucket(endTime, task);
0465: taskAdded = true;
0466: } else {
0467: int[] ats = ar.getAspectTypes();
0468: int endInd = csvWriter.getIndexForType(ats,
0469: AspectType.END_TIME);
0470: Enumeration phasedResults = ar.getPhasedResults();
0471: while (phasedResults.hasMoreElements()) {
0472: double[] results = (double[]) phasedResults
0473: .nextElement();
0474: long phasedEndTime;
0475: phasedEndTime = (long) results[endInd];
0476: addTaskToBucket(phasedEndTime, task);
0477: taskAdded = true;
0478: }
0479: }
0480: }
0481: }
0482: if (!taskAdded) {
0483: long endTime = taskUtils.getReportedEndTime(task);
0484: addTaskToBucket(endTime, task);
0485: }
0486: }
0487:
0488: private void addTaskToBucket(long endTime, Task task) {
0489: // probably can factor this out into a separate method.
0490: int bucket = convertTimeToBucket(endTime, false);
0491: if (bucket < getStartBucket()) {
0492: if (logger.isErrorEnabled()) {
0493: logger
0494: .error("addRefillRequisition not adding old requisition. startBucket is "
0495: + TimeUtils
0496: .dateString(convertBucketToTime(getStartBucket()))
0497: + ", task " + taskUtils.taskDesc(task));
0498: }
0499: return;
0500: }
0501: while (bucket >= refillRequisitions.size()) {
0502: refillRequisitions.add(null);
0503: }
0504:
0505: //MWD Debugging
0506: List refills = (List) refillRequisitions.get(bucket);
0507: if (refills == null) {
0508: refills = new ArrayList();
0509: refillRequisitions.set(bucket, refills);
0510: }
0511: refills.add(task);
0512: }
0513:
0514: public int getLastWithdrawBucket() {
0515: Iterator list = customerHash.values().iterator();
0516: long lastSeen = convertBucketToTime(1);
0517: while (list.hasNext()) {
0518: long time = ((Long) list.next()).longValue();
0519: if (lastSeen < time) {
0520: lastSeen = time;
0521: }
0522: }
0523: return convertTimeToBucket(lastSeen, false);
0524: }
0525:
0526: private int getInitialLevelDemandEndBucket() {
0527: if (myPG.getSupplierArrivalTime() == -1) {
0528: return -1;
0529: }
0530: int supplierAvailableBucket = convertTimeToBucket(myPG
0531: .getSupplierArrivalTime(), false);
0532:
0533: //At the top of the supply chain 191-ORDBN has a
0534: //supplier available bucket that is 8/15 which is before its
0535: //arrival time. The supplier is only truly available to you
0536: //once you arrive.
0537: int arrivalBucket = convertTimeToBucket(arrivalTime, false);
0538: if (supplierAvailableBucket < arrivalBucket) {
0539: supplierAvailableBucket = arrivalBucket;
0540: }
0541:
0542: //Test case where first bucket of demand is beyone the supplierAvailableBucket
0543: //then we should make sure we cover the 1st days of demand with the ost amount of demand.
0544: int firstDemandBucket = getFirstProjectWithdrawBucket();
0545: if (firstDemandBucket > supplierAvailableBucket) {
0546: supplierAvailableBucket = firstDemandBucket;
0547: }
0548:
0549: //The end of the horizon lookahead to compute the initial level
0550: int initialLevelDemandEndBucket = supplierAvailableBucket + ost
0551: + criticalLevel;
0552:
0553: return initialLevelDemandEndBucket;
0554: }
0555:
0556: private double getDemandDerivedInitialLevel() {
0557: //Not sure if this should be false or true.
0558: int startBucket = convertTimeToBucket(arrivalTime, false);
0559: //This includes the critical level and order ship time.
0560: int endBucket = getInitialLevelDemandEndBucket();
0561: if (endBucket == -1) {
0562: return -1.0;
0563: }
0564: double totalDemand = 0.0;
0565: for (int i = startBucket; i <= endBucket; i++) {
0566: totalDemand += getProjectedDemand(i);
0567: }
0568: //Round up, no matter, to the next larger whole integer
0569: //This is done mainly for class IX components, whose demand
0570: //is fractional - but we reorder actuals in whole numbers.
0571: totalDemand = Math.ceil(totalDemand);
0572: return totalDemand;
0573: }
0574:
0575: public int getFirstProjectWithdrawBucket() {
0576: Iterator list = customerHash.values().iterator();
0577: // if we don't have any actual demand signal with -1
0578: if (!list.hasNext()) {
0579: return -1;
0580: }
0581: long firstSeen = convertBucketToTime(dueOutList.size() - 1);
0582: while (list.hasNext()) {
0583: long time = ((Long) list.next()).longValue();
0584: if (firstSeen > time) {
0585: firstSeen = time;
0586: }
0587: }
0588: // return the bucket after the first seen actual's end time bucket
0589: return (convertTimeToBucket(firstSeen, false) + 1);
0590: }
0591:
0592: public int getLastRefillRequisition() {
0593: int lastRefill = -1;
0594: for (int i = 0; i < refillRequisitions.size(); i++) {
0595: List refills = (List) refillRequisitions.get(i);
0596: if ((refills != null) && (refills.size() > 0)) {
0597: //The last refill is actually the target date for it's arrival,
0598: //because thats what the refill generator is planning on.
0599: for (int j = 0; j < refills.size(); j++) {
0600: Task task = (Task) refills.get(j);
0601: //taskUtils.getEndTime() returns the preference (the best time)
0602: int endBucket = convertTimeToBucket(taskUtils
0603: .getEndTime(task), false);
0604: lastRefill = Math.max(endBucket, lastRefill);
0605: }
0606: //Put above for loop and code in to replace line below this
0607: //because we were counting all the projections from an early arrival
0608: //to the request date of a last refill.
0609: //lastRefill = i;
0610: }
0611: }
0612: return lastRefill;
0613: }
0614:
0615: public int getLastDemandBucket() {
0616: return dueOutList.size() - 1;
0617: }
0618:
0619: public void setEndOfLevelSixBucket(int bucket) {
0620: endOfLevelSixBucket = bucket;
0621: }
0622:
0623: public void setArrivalTime(long anArrivalTime) {
0624: this .arrivalTime = anArrivalTime;
0625: }
0626:
0627: public long getArrivalTime() {
0628: return arrivalTime;
0629: }
0630:
0631: public void setStartCDay(long startCTime) {
0632: this .startCDay = startCTime;
0633: }
0634:
0635: public int getEndOfLevelSixBucket() {
0636: return endOfLevelSixBucket;
0637: }
0638:
0639: public void addRefillProjection(Task task) {
0640: long start = taskUtils.getReportedStartTime(task);
0641: long end = taskUtils.getReportedEndTime(task);
0642: // Test for Failed Dispositions Bug #2033
0643: if (start == 0L) {
0644: return;
0645: }
0646: int bucket_start = convertTimeToBucket(start, false);
0647: int bucket_end = convertTimeToBucket(end, true);
0648: // StartBucket for the inventory is the first bucket which has not seen any deletions
0649: // It is possible to have a case where a projection is added to buckets in the past
0650: // because the allocation results on a projection can change until the end of the
0651: // task which may still be in the future. Allow projections to be added to buckets
0652: // in the past but not to buckets before the start bucket of the inventory.
0653: if (bucket_start < getStartBucket()) {
0654: if (bucket_end < getStartBucket()) {
0655: if (logger.isErrorEnabled()) {
0656: logger
0657: .error("addRefillProjection not adding old projection. startBucket is "
0658: + TimeUtils
0659: .dateString(convertBucketToTime(getStartBucket()))
0660: + ", task "
0661: + taskUtils.taskDesc(task));
0662:
0663: }
0664: return;
0665: } else {
0666: if (logger.isInfoEnabled()) {
0667: logger
0668: .info("addRefillProjection not adding projection to deleted buckets. "
0669: + "startBucket is "
0670: + TimeUtils
0671: .dateString(convertBucketToTime(getStartBucket()))
0672: + ", task "
0673: + taskUtils.taskDesc(task));
0674: }
0675: bucket_start = getStartBucket();
0676: }
0677: }
0678:
0679: while (bucket_end >= refillProjections.size()) {
0680: refillProjections.add(null);
0681: }
0682: for (int i = bucket_start; i < bucket_end; i++) {
0683: List refills = (List) refillProjections.get(i);
0684: if (refills == null) {
0685: refills = new ArrayList();
0686: refillProjections.set(i, refills);
0687: }
0688: if (!refills.contains(task)) {
0689: refills.add(task);
0690: }
0691: }
0692: }
0693:
0694: public void removeRefillProjection(Task task) {
0695: if (task.isDeleted()) {
0696: int end_bucket = convertTimeToBucket(taskUtils
0697: .getReportedEndTime(task), true);
0698: if (logger.isDebugEnabled()) {
0699: logger.debug("Removing task "
0700: + taskUtils.taskDesc(task));
0701: }
0702: setDeletionBucket(end_bucket - 1);
0703: }
0704: removeTask(refillProjections, task);
0705: }
0706:
0707: public void removeRefillRequisition(Task task) {
0708: if (task.isDeleted()) {
0709: int end_bucket = convertTimeToBucket(taskUtils
0710: .getReportedEndTime(task), false);
0711: if (logger.isDebugEnabled()) {
0712: logger.debug("Removing task "
0713: + taskUtils.taskDesc(task));
0714: }
0715: setDeletionBucket(end_bucket);
0716: }
0717: removeTask(refillRequisitions, task);
0718: }
0719:
0720: public List clearRefillTasks(final Date now) {
0721: // remove uncommitted refill tasks. Add all removed
0722: // tasks to a second list that will be returned
0723: // for comparison
0724: UnaryPredicate p = new UnaryPredicate() {
0725: public boolean execute(Object o) {
0726: return shouldClearRefill((Task) o, now);
0727: }
0728: };
0729: return removeTasks(refillRequisitions, p);
0730: }
0731:
0732: private boolean shouldClearRefill(Task t, Date now) {
0733: return (t != null && t.beforeCommitment(now));
0734: }
0735:
0736: // clear the projections
0737: // and return the removed tasks for comparison
0738: // also keep a list of overlapping refill projection tasks
0739: public List clearRefillProjectionTasks(final long now) {
0740: overlappingRefillProjections.clear();
0741:
0742: UnaryPredicate p = new UnaryPredicate() {
0743: public boolean execute(Object o) {
0744: return shouldClearProjection((Task) o, now);
0745: }
0746: };
0747: return removeTasks(refillProjections, p);
0748: }
0749:
0750: private boolean shouldClearProjection(Task t, long now) {
0751: if (t == null) {
0752: return false;
0753: }
0754: long start = taskUtils.getStartTime(t);
0755: if (start >= now) {
0756: return true;
0757: }
0758: if ((start < now) && (taskUtils.getEndTime(t) > now)) {
0759: //this task spans now - shorten it
0760: if (!overlappingRefillProjections.contains(t)) {
0761: overlappingRefillProjections.add(t);
0762: }
0763: }
0764: return false;
0765: }
0766:
0767: private void removeTask(List allTasks, Task task) {
0768: for (int i = 0; i < allTasks.size(); i++) {
0769: List tasks = (List) allTasks.get(i);
0770: if (tasks != null && tasks.remove(task) && tasks.size() < 1) {
0771: allTasks.set(i, null);
0772: }
0773: }
0774: }
0775:
0776: // a generic task removal function:
0777: private List removeTasks(List allTasks, UnaryPredicate pred) {
0778: List removed = new ArrayList();
0779: for (int i = 0; i < allTasks.size(); i++) {
0780: List tasks = (List) allTasks.get(i);
0781: if (tasks == null) {
0782: continue;
0783: }
0784: List pending = null;
0785: for (int j = 0; j < tasks.size(); j++) {
0786: Task t = (Task) tasks.get(j);
0787: if (!pred.execute(t)) {
0788: continue;
0789: }
0790: if (pending == null) {
0791: pending = new ArrayList(tasks.size() - j);
0792: }
0793: if (!pending.contains(t)) {
0794: pending.add(t);
0795: }
0796: if (!removed.contains(t)) {
0797: removed.add(t);
0798: }
0799: }
0800: if (pending != null) {
0801: tasks.removeAll(pending);
0802: }
0803: if (tasks.isEmpty()) {
0804: allTasks.set(i, null);
0805: }
0806: }
0807: return removed;
0808: }
0809:
0810: public List getOverlappingRefillProjections() {
0811: return (List) ((ArrayList) overlappingRefillProjections)
0812: .clone();
0813: }
0814:
0815: private List getFlattenedList(List listOLists) {
0816: List uniqueTasks = new ArrayList();
0817: for (int i = 0; i < listOLists.size(); i++) {
0818: List list = (List) listOLists.get(i);
0819: if (list != null) {
0820: for (int j = 0; j < list.size(); j++) {
0821: Task task = (Task) list.get(j);
0822: if (!uniqueTasks.contains(task)) {
0823: uniqueTasks.add(task);
0824: }
0825: }
0826: }
0827: }
0828: return uniqueTasks;
0829: }
0830:
0831: private List deepClone(List listOLists) {
0832: List clone = new ArrayList(listOLists.size());
0833: for (int i = 0; i < listOLists.size(); i++) {
0834: List list = (List) listOLists.get(i);
0835: if (list != null) {
0836: list = (List) ((ArrayList) list).clone();
0837: }
0838: clone.add(i, list);
0839: }
0840: return clone;
0841: }
0842:
0843: public ArrayList getRefillRequisitions() {
0844: return (ArrayList) ((ArrayList) refillRequisitions).clone();
0845: }
0846:
0847: public ArrayList getRefillProjection(int bucket) {
0848: // make sure the bucket doesn't cause an array out of bounds
0849: if (bucket < refillProjections.size()) {
0850: return (ArrayList) refillProjections.get(bucket);
0851: } else {
0852: return null;
0853: }
0854: }
0855:
0856: public double getCriticalLevel(int bucket) {
0857: if (compute_critical_levels) {
0858: computeCriticalLevels();
0859: compute_critical_levels = false;
0860: }
0861: if ((bucket >= 0) && (bucket < criticalLevelsArray.length)) {
0862: return criticalLevelsArray[bucket];
0863: }
0864: return 0.0;
0865: }
0866:
0867: public void recalculateInitialLevel() {
0868: double newInitialLevel = getDemandDerivedInitialLevel();
0869: if ((recalculate_initial_level) && (getStartBucket() == 0)) {
0870: if (newInitialLevel != -1.0) {
0871: inventoryLevelsArray[0] = newInitialLevel;
0872: }
0873: }
0874: recalculate_initial_level = false;
0875: }
0876:
0877: private double[] computeCriticalLevels() {
0878: if (regenerate_projected_demand) {
0879: regenerateProjectedDemandList();
0880: regenerate_projected_demand = false;
0881: }
0882: double Ci;
0883: criticalLevelsArray = new double[projectedDemandArray.length];
0884: // criticalLevel falls within a single bucket
0885: if (criticalLevel == 1) {
0886: for (int i = 0; // i = getStartBucket();
0887: i < projectedDemandArray.length; i++) {
0888: Ci = getProjectedDemand(i);
0889: criticalLevelsArray[i] = Ci;
0890: }
0891: } else { // criticalLevel spans multiple buckets
0892: // int start = (getStartBucket() > 1) ? getStartBucket() : 1;
0893: int start = 1;
0894: int buckets = criticalLevel;
0895: Ci = 0.0;
0896: for (int i = start; i <= buckets; i++) {
0897: Ci += getProjectedDemand(i);
0898: }
0899: criticalLevelsArray[0] = Ci;
0900: for (int i = start; i < projectedDemandArray.length
0901: - buckets - 1; i++) {
0902: Ci = Ci - getProjectedDemand(i)
0903: + getProjectedDemand(i + buckets);
0904: if (Ci < 0.0) {
0905: Ci = 0.0;
0906: }
0907: criticalLevelsArray[i] = Ci;
0908: }
0909: // TODO may want to test start bucket here too but I'm on the fence
0910: for (int i = projectedDemandArray.length - buckets - 1; i < projectedDemandArray.length; i++) {
0911: Ci = Ci - getProjectedDemand(i);
0912: if (Ci < 0.0) {
0913: Ci = 0.0;
0914: }
0915: criticalLevelsArray[i] = Ci;
0916: }
0917: }
0918:
0919: //No critical level before we arrive in theatre.
0920: //We're not supposed to order refills before we arrive in theatre.
0921: int arrivalTimeBucket = convertTimeToBucket(arrivalTime, false);
0922: for (int i = getStartBucket(); i < arrivalTimeBucket; i++) {
0923: criticalLevelsArray[i] = 0.0;
0924: }
0925:
0926: return criticalLevelsArray;
0927: }
0928:
0929: public double getLevel(int bucket) {
0930: if (bucket < 0) {
0931: logger.error("getLevel asked for level at bucket " + bucket
0932: + " when inventoryLevelsArray has length "
0933: + inventoryLevelsArray.length, new Throwable());
0934: return inventoryLevelsArray[0];
0935: }
0936: if (bucket >= inventoryLevelsArray.length) {
0937: return inventoryLevelsArray[inventoryLevelsArray.length - 1];
0938: }
0939: return inventoryLevelsArray[bucket];
0940: }
0941:
0942: public void setLevel(int bucket, double value) {
0943: if (bucket < getStartBucket()) {
0944: if (bucket < 0) {
0945: if (logger.isErrorEnabled()) {
0946: logger.error("setLevel called with bucket "
0947: + bucket + " when startBucket is "
0948: + getStartBucket(), new Throwable());
0949: }
0950: }
0951: if (logger.isDebugEnabled()) {
0952: logger.debug("setLevel called with bucket " + bucket
0953: + " when startBucket is " + getStartBucket());
0954: }
0955: return;
0956: }
0957: if (bucket >= inventoryLevelsArray.length) {
0958: inventoryLevelsArray = expandArray(inventoryLevelsArray,
0959: bucket);
0960: }
0961: //Potentially if you reset the first inventory level bucket, you
0962: //will want to reset it on the next refillGenerator call to recalculate
0963: if (bucket == 0) {
0964: recalculate_initial_level = true;
0965: }
0966: inventoryLevelsArray[bucket] = value;
0967: }
0968:
0969: public void setTarget(int bucket, double value) {
0970: if (bucket < getStartBucket()) {
0971: if (bucket < 0) {
0972: if (logger.isErrorEnabled()) {
0973: logger.error("setTarget called with bucket "
0974: + bucket + " and value " + value,
0975: new Throwable());
0976: }
0977: }
0978: if (logger.isDebugEnabled()) {
0979: logger.debug("setTarget called with bucket " + bucket
0980: + " when startBucket is " + getStartBucket());
0981: }
0982: return;
0983: }
0984: // The intention of the List is to hold values for the buckets
0985: // that have target levels and hold nulls for those buckets that
0986: // do not have a target level
0987: int len = targetLevelsList.size();
0988: if (bucket >= len) {
0989: for (int i = len; i < bucket + 20; i++) {
0990: targetLevelsList.add(null);
0991: }
0992: }
0993: targetLevelsList.set(bucket, new Double(value));
0994: }
0995:
0996: public void clearTargetLevels(int startBucket) {
0997: if (startBucket < 0) {
0998: if (logger.isErrorEnabled()) {
0999: logger.error(
1000: "clearTargetLevels called with startBucket "
1001: + startBucket, new Throwable());
1002: }
1003: startBucket = 0;
1004: }
1005: if (logger.isDebugEnabled()) {
1006: logger
1007: .debug(getOrgName()
1008: + " clearTargetLevels called with bucket "
1009: + TimeUtils
1010: .dateString(convertBucketToTime(startBucket))
1011: + " when start bucket is "
1012: + TimeUtils
1013: .dateString(convertBucketToTime(getStartBucket())));
1014: }
1015: // Below will be replaced by the following once debugging is complete
1016: // startBucket = (startBucket < getStartBucket()) ? getStartBucket() : startBucket;
1017: if (startBucket < getStartBucket()) {
1018: startBucket = getStartBucket();
1019: }
1020:
1021: // Clear target levels from the given bucket to end of array
1022: int len = targetLevelsList.size();
1023: for (int i = startBucket; i < len; i++) {
1024: targetLevelsList.set(i, null);
1025: }
1026: }
1027:
1028: public void updateRefillRequisition(Task task) {
1029: removeRefillRequisition(task);
1030: addRefillRequisition(task);
1031: }
1032:
1033: public void updateRefillProjection(Task task) {
1034: removeRefillProjection(task);
1035: addRefillProjection(task);
1036: }
1037:
1038: public void updateWithdrawRequisition(Task task) {
1039: removeWithdrawRequisition(task);
1040: addWithdrawRequisition(task);
1041: }
1042:
1043: public void updateWithdrawProjection(Task task) {
1044: removeWithdrawProjection(task);
1045: addWithdrawProjection(task);
1046: }
1047:
1048: public double getProjectedDemand(int bucket) {
1049: if (regenerate_projected_demand) {
1050: regenerateProjectedDemandList();
1051: regenerate_projected_demand = false;
1052: }
1053: if ((bucket >= projectedDemandArray.length) || (bucket < 0)) {
1054: return 0.0;
1055: }
1056: return projectedDemandArray[bucket];
1057: }
1058:
1059: public Collection getProjectedDemandTasks(int bucket) {
1060: return getTasks(bucket, Constants.Verb.PROJECTWITHDRAW);
1061: }
1062:
1063: public double getActualDemand(int bucket) {
1064: if (bucket >= dueOutList.size()) {
1065: return 0.0;
1066: }
1067: long bucket_start = convertBucketToTime(bucket);
1068: long bucket_end = bucket_start + msec_per_bucket;
1069:
1070: double actualDemand = 0.0;
1071: for (Iterator list = ((List) dueOutList.get(bucket)).iterator(); list
1072: .hasNext();) {
1073: Task task = (Task) list.next();
1074: long firstProjectionTime = getFirstProjectionTime(task);
1075: actualDemand += taskUtils.getActualDemand(task,
1076: bucket_start, bucket_end, firstProjectionTime);
1077: }
1078: return actualDemand;
1079: }
1080:
1081: public Collection getActualDemandTasks(int bucket) {
1082: List demand = new ArrayList();
1083: if (bucket >= dueOutList.size()) {
1084: return demand;
1085: }
1086: long end_of_bucket = (convertBucketToTime(bucket + 1) - 1);
1087: Iterator dueOutIter = ((List) dueOutList.get(bucket))
1088: .iterator();
1089: while (dueOutIter.hasNext()) {
1090: Task task = (Task) dueOutIter.next();
1091: if (task.getVerb().equals(Constants.Verb.PROJECTWITHDRAW)) {
1092: long firstProjectionTime = getFirstProjectionTime(task);
1093: long start = (long) PluginHelper
1094: .getPreferenceBestValue(task,
1095: AspectType.START_TIME);
1096: long end = (long) PluginHelper.getPreferenceBestValue(
1097: task, AspectType.END_TIME);
1098: start = Math.max(start, firstProjectionTime);
1099: if ((start >= end) || (start >= end_of_bucket)) {
1100: // task is outside bucket
1101: continue;
1102: }
1103: }
1104: demand.add(task);
1105: }
1106: return demand;
1107: }
1108:
1109: public double getReorderPeriod() {
1110: return reorderPeriod;
1111: }
1112:
1113: /**
1114: Ignore projected demand that occurs before customer switchover day.
1115: **/
1116: public synchronized long getEffectiveProjectionStart(Task task,
1117: long start) {
1118: long firstProjectionTime = getFirstProjectionTime(task);
1119: return Math.max(firstProjectionTime, start);
1120: }
1121:
1122: private long getFirstProjectionTime(Task task) {
1123: Object org = taskUtils.getCustomer(task);
1124: if (org != null) {
1125: Long lastActualSeen = (Long) customerHash.get(org);
1126: if (lastActualSeen != null) {
1127: long firstProjectionTime = lastActualSeen.longValue()
1128: + msec_per_bucket;
1129: firstProjectionTime = truncateToBucketStart(firstProjectionTime);
1130: return firstProjectionTime;
1131: }
1132: }
1133: return -1;
1134: }
1135:
1136: private Collection getTasks(int bucket, String verb) {
1137: List task_list = new ArrayList();
1138: if (bucket < dueOutList.size()) {
1139: Iterator list = ((List) dueOutList.get(bucket)).iterator();
1140: while (list.hasNext()) {
1141: Task task = (Task) list.next();
1142: if (task.getVerb().equals(verb)) {
1143: task_list.add(task);
1144: }
1145: }
1146: }
1147: return task_list;
1148: }
1149:
1150: public Collection getWithdrawTasks(int bucket) {
1151: return getTasks(bucket, Constants.Verb.WITHDRAW);
1152: }
1153:
1154: public Collection getProjectWithdrawTasks(int bucket) {
1155: return getTasks(bucket, Constants.Verb.PROJECTWITHDRAW);
1156: }
1157:
1158: public void rebuildCustomerHash() {
1159: customerHash.clear();
1160: // first run through the projectwithdraws and make the lastActualSeen the
1161: // day before the start of the earliest projectwithdraw. We won't really have
1162: // gotten an actual then, but its the only way to make sure that a customer who
1163: // ONLY sends projectwithdraws (instead of atleast one withdraw) actually
1164: // gets an entry put in the customer has for itself.
1165: for (int i = dueOutList.size() - 1; i >= 0; i--) {
1166: List list = (ArrayList) dueOutList.get(i);
1167: if (list.isEmpty()) {
1168: continue;
1169: }
1170: Iterator listIter = list.iterator();
1171: while (listIter.hasNext()) {
1172: Task task = (Task) listIter.next();
1173: if (!task.getVerb().equals(
1174: Constants.Verb.PROJECTWITHDRAW)) {
1175: continue;
1176: }
1177: Object org = TaskUtils.getCustomer(task);
1178: long startTime = taskUtils.getStartTime(task);
1179: // now make end time the bucket before the start time of the first projectwithdraw
1180: long endTime = startTime - (msec_per_bucket * 2);
1181: Long lastActualSeen = (Long) customerHash.get(org);
1182: if ((lastActualSeen == null)
1183: || (endTime < lastActualSeen.longValue())) {
1184: customerHash.put(org, new Long(endTime));
1185: }
1186: }
1187: }
1188:
1189: // now reset the customer has values for those customers that really did
1190: // send atleast one withdraw task
1191: for (int i = dueOutList.size() - 1; i >= 0; i--) {
1192: List list = (List) dueOutList.get(i);
1193: if (list.isEmpty()) {
1194: continue;
1195: }
1196: Iterator listIter = list.iterator();
1197: while (listIter.hasNext()) {
1198: Task task = (Task) listIter.next();
1199: if (!task.getVerb().equals(Constants.Verb.WITHDRAW)) {
1200: continue;
1201: }
1202: Object org = TaskUtils.getCustomer(task);
1203: long endTime = taskUtils.getEndTime(task);
1204: Long lastActualSeen = (Long) customerHash.get(org);
1205: if ((lastActualSeen == null)
1206: || (endTime > lastActualSeen.longValue())) {
1207: customerHash.put(org, new Long(endTime));
1208: }
1209: }
1210: }
1211: }
1212:
1213: public HashMap getCustomerHash() {
1214: return (HashMap) customerHash;
1215: }
1216:
1217: public void logAllToCSVFile(long aCycleStamp) {
1218: if (csvLogger != null) {
1219: csvWriter.logToExcelOutput(myPG, aCycleStamp);
1220: }
1221: }
1222:
1223: public long getStartTime() {
1224: return startTime;
1225: }
1226:
1227: public int getStartBucket() {
1228: return deletionBucket + 1;
1229: }
1230:
1231: public ArrayList getProjWithdrawList() {
1232: return (ArrayList) projWithdrawList;
1233: }
1234:
1235: public ArrayList getWithdrawList() {
1236: return (ArrayList) withdrawList;
1237: }
1238:
1239: public ArrayList getProjSupplyList() {
1240: return (ArrayList) getFlattenedList(projSupplyList);
1241: }
1242:
1243: public ArrayList getSupplyList() {
1244: return (ArrayList) getFlattenedList(supplyList);
1245: }
1246:
1247: public Schedule getBufferedCritLevels() {
1248: return bufferedCriticalLevels;
1249: }
1250:
1251: public Schedule getBufferedInvLevels() {
1252: return bufferedInventoryLevels;
1253: }
1254:
1255: public Schedule getBufferedTargetLevels() {
1256: return bufferedTargetLevels;
1257: }
1258:
1259: public ArrayList getActualDemandTasksList() {
1260: return (ArrayList) actualDemandTasksList;
1261: }
1262:
1263: public ShortfallInventory checkForShortfall(String invID,
1264: String unitOfIssue) {
1265: ShortfallInventory shortfallInv = newShortfallInventory(invID,
1266: unitOfIssue);
1267:
1268: int numDemandProj = countProjFailures(getProjWithdrawList(),
1269: true, true);
1270: int numPermDemandProj = countProjFailures(
1271: getProjWithdrawList(), true, false);
1272: shortfallInv.setNumDemandProj(numDemandProj);
1273: shortfallInv.setNumTempDemandProj(numDemandProj
1274: - numPermDemandProj);
1275:
1276: int numResupplyProj = countProjFailures(getProjSupplyList(),
1277: false, true);
1278: int numPermResupplyProj = countProjFailures(
1279: getProjSupplyList(), false, false);
1280: shortfallInv.setNumResupplyProj(numResupplyProj);
1281: shortfallInv.setNumTempResupplyProj(numResupplyProj
1282: - numPermResupplyProj);
1283:
1284: int numDemandSupply = countActualShortfall(getWithdrawList(),
1285: true);
1286: int numPermDemandSupply = countActualShortfall(
1287: getWithdrawList(), false);
1288: shortfallInv.setNumDemandSupply(numDemandSupply);
1289: shortfallInv.setNumTempDemandSupply(numDemandSupply
1290: - numPermDemandSupply);
1291: int numResupplySupply = countActualShortfall(getSupplyList(),
1292: true);
1293: int numPermResupplySupply = countActualShortfall(
1294: getSupplyList(), false);
1295: shortfallInv.setNumResupplySupply(numResupplySupply);
1296: shortfallInv.setNumTempResupplySupply(numResupplySupply
1297: - numPermResupplySupply);
1298:
1299: if (shortfallInv.getNumTotalShortfall() <= 0) {
1300: return null;
1301: }
1302: addShortfallPeriods(shortfallInv);
1303: return shortfallInv;
1304: }
1305:
1306: protected ShortfallInventory newShortfallInventory(String invId,
1307: String unitOfIssue) {
1308: return new ShortfallInventory(invId, unitOfIssue);
1309: }
1310:
1311: protected void addShortfallPeriods(ShortfallInventory shortfallInv) {
1312: int startBucket = getStartBucket();
1313: int endBucket = getLastDemandBucket();
1314:
1315: long startOfPeriod = -1;
1316: long endOfPeriod = -1;
1317: boolean inShortfallPeriod = false;
1318:
1319: for (int i = startBucket; i <= endBucket; i++) {
1320: double level = getLevel(i);
1321: if (level == 0.0) {
1322: if (!inShortfallPeriod) {
1323: startOfPeriod = convertBucketToTime(i);
1324: inShortfallPeriod = true;
1325: }
1326: } else {
1327: if (inShortfallPeriod) {
1328: //The bucket before the current one was the last shortfall period bucket. Therefore i-1
1329: endOfPeriod = convertBucketToTime(i) - 1;
1330: ShortfallPeriod newPeriod = newShortfallPeriod(
1331: startOfPeriod, endOfPeriod);
1332: calculateShortfallPeriod(newPeriod,
1333: getProjWithdrawList());
1334: calculateShortfallPeriod(newPeriod,
1335: getWithdrawList());
1336: shortfallInv.addShortfallPeriod(newPeriod);
1337: inShortfallPeriod = false;
1338: }
1339: }
1340: }
1341:
1342: if (inShortfallPeriod) {
1343: //The bucket before the current one was the last shortfall period bucket. Therefore i-1
1344: endOfPeriod = convertBucketToTime(endBucket) - 1;
1345: ShortfallPeriod newPeriod = newShortfallPeriod(
1346: startOfPeriod, endOfPeriod);
1347: calculateShortfallPeriod(newPeriod, getProjWithdrawList());
1348: calculateShortfallPeriod(newPeriod, getWithdrawList());
1349: shortfallInv.addShortfallPeriod(newPeriod);
1350: }
1351: }
1352:
1353: protected ShortfallPeriod newShortfallPeriod(long start, long end) {
1354: return new ShortfallPeriod(start, end);
1355: }
1356:
1357: protected void calculateShortfallPeriod(
1358: ShortfallPeriod shortPeriod, List tasks) {
1359: double totalDemand = 0.0d;
1360: double totalFilled = 0.0d;
1361: for (Iterator taskIt = tasks.iterator(); taskIt.hasNext();) {
1362: Task t = (Task) taskIt.next();
1363:
1364: long minStart = shortPeriod.getStartTime();
1365: long maxEnd = shortPeriod.getEndTime();
1366: if (taskUtils.isProjection(t)) {
1367: minStart = Math
1368: .max(minStart, getFirstProjectionTime(t));
1369: }
1370:
1371: totalDemand += taskUtils.calculateDemand(t, minStart,
1372: maxEnd);
1373: totalFilled += taskUtils.calculateFilled(t, minStart,
1374: maxEnd);
1375: }
1376:
1377: shortPeriod.setTotalDemand(shortPeriod.getTotalDemand()
1378: + totalDemand);
1379: shortPeriod.setTotalFilled(shortPeriod.getTotalFilled()
1380: + totalFilled);
1381: }
1382:
1383: protected int countProjFailures(List taskList, boolean isDemand,
1384: boolean includeTemps) {
1385: int ctr = 0;
1386: for (Iterator it = taskList.iterator(); it.hasNext();) {
1387: Task t = (Task) it.next();
1388: PlanElement pe = t.getPlanElement();
1389: if (pe == null) {
1390: continue;
1391: }
1392: long minStart = (isDemand ? getFirstProjectionTime(t)
1393: : convertBucketToTime(getLastRefillRequisition() + 1));
1394: if (taskUtils.isProjFailure(t, minStart, includeTemps)) {
1395: ctr++;
1396: }
1397: }
1398: return ctr;
1399: }
1400:
1401: protected int countActualShortfall(List taskList,
1402: boolean includeTemps) {
1403: int ctr = 0;
1404: for (Iterator it = taskList.iterator(); it.hasNext();) {
1405: Task t = (Task) it.next();
1406: if (taskUtils.isActualShortfall(t, includeTemps)) {
1407: ctr++;
1408: }
1409: }
1410: return ctr;
1411: }
1412:
1413: protected double totalShortfall(List taskList) {
1414: double requested = 0;
1415: double granted = 0;
1416: for (Iterator it = taskList.iterator(); it.hasNext();) {
1417: Task t = (Task) it.next();
1418: requested += taskUtils.getTotalQuantity(t);
1419: granted += taskUtils.getGrantedQuantity(t);
1420: }
1421: double diff = granted - requested;
1422: if ((diff <= 0) && (diff > -0.00005)) {
1423: diff = 0.0;
1424: }
1425: return diff;
1426: }
1427:
1428: /**
1429: * Take the incomming time and convert it to the beginning of the bucket
1430: * in which it falls
1431: */
1432: public long truncateToBucketStart(long aTime) {
1433: int bucket = convertTimeToBucket(aTime, false);
1434: return convertBucketToTime(bucket);
1435: }
1436:
1437: /**
1438: * Convert a time (long) into a bucket of this inventory that can be
1439: * used to index duein/out vectors, levels, etc.
1440: **/
1441: public int convertTimeToBucket(long time, boolean partialBuckets) {
1442: if (time < 0) {
1443: logger.error("convertTimeToBucket: Got negative time "
1444: + TimeUtils.dateString(time), new Throwable());
1445: return 0;
1446: }
1447: int this Bucket = (int) (time / msec_per_bucket);
1448: if (partialBuckets) {
1449: // FCS - HOURLY : Added this code from Bug #2413
1450: if ((time % msec_per_bucket) > 0.0) {
1451: this Bucket += 1;
1452: }
1453: // FCS - HOURLY : End added code
1454: }
1455: if (this Bucket < timeZero) {
1456: logger
1457: .error("convertTimeToBucket: For Org: "
1458: + getOrgName() + "-" + getSupplyType()
1459: + " item: " + getItemName()
1460: + " - thisBucket(" + this Bucket
1461: + ") - timeZero(" + timeZero
1462: + ") is < 0! (" + (this Bucket - timeZero)
1463: + ") when started with time "
1464: + TimeUtils.dateString(time) + "!",
1465: new Throwable());
1466: return 0;
1467: }
1468: return this Bucket - timeZero;
1469: }
1470:
1471: /**
1472: * Convert a bucket (int) into the start time of the bucket.
1473: * The end time of the bucket must be inferred from the
1474: * next sequential bucket's start time (non-inclusive).
1475: **/
1476: public long convertBucketToTime(int bucket) {
1477: if (bucket < 0) {
1478: logger.error("convertBucketToTime: Got negative bucket "
1479: + bucket, new Throwable());
1480: return 0;
1481: }
1482: return (bucket + timeZero) * msec_per_bucket;
1483: }
1484:
1485: /** @return long The amount of ms the bucket spans **/
1486: public long getBucketMillis() {
1487: return msec_per_bucket;
1488: }
1489:
1490: // Grow the array by 50% at a time, but at least enough to cover
1491: // the incoming request
1492: private double[] expandArray(double[] doubleArray, int newMinLength) {
1493: if (newMinLength < doubleArray.length)
1494: return doubleArray;
1495:
1496: double biggerArray[] = new double[Math.max(newMinLength,
1497: (int) (doubleArray.length * 1.5))];
1498: for (int i = 0; i < doubleArray.length; i++) {
1499: biggerArray[i] = doubleArray[i];
1500: }
1501: return biggerArray;
1502: }
1503:
1504: public int getCriticalLevel() {
1505: return criticalLevel;
1506: }
1507:
1508: public void takeSnapshot(Inventory inventory) {
1509: List tmpProjWdraw = new ArrayList();
1510: List tmpWdraw = new ArrayList();
1511: List tmpProjResupply = new ArrayList();
1512:
1513: Iterator due_outs;
1514: for (int i = 0; i < dueOutList.size(); i++) {
1515: due_outs = ((List) dueOutList.get(i)).iterator();
1516: while (due_outs.hasNext()) {
1517: Task task = (Task) due_outs.next();
1518: if (task.getVerb().equals(Constants.Verb.WITHDRAW)) {
1519: tmpWdraw.add(task);
1520: } else { // PROJECTWITHDRAW
1521: if (!tmpProjWdraw.contains(task)) {
1522: tmpProjWdraw.add(task);
1523: }
1524: }
1525: }
1526: }
1527: projWithdrawList = tmpProjWdraw;
1528: withdrawList = tmpWdraw;
1529:
1530: projSupplyList = deepClone(refillProjections);
1531: supplyList = deepClone(refillRequisitions);
1532:
1533: // MWD took this out when converted list to a schedule.
1534: // bufferedTargetLevels = (List) targetLevelsList.clone();
1535:
1536: List tmpActualDemandTasksList = new ArrayList();
1537: for (int i = 0; i < dueOutList.size(); i++) {
1538: tmpActualDemandTasksList.add(getActualDemandTasks(i));
1539: }
1540: actualDemandTasksList = tmpActualDemandTasksList;
1541:
1542: Vector list = new Vector();
1543: long start = convertBucketToTime(0);
1544: //the length of the critical level and inventory level
1545: //arrays should match up
1546: int length = Math.max(getMeaningfulLength(criticalLevelsArray),
1547: getMeaningfulLength(inventoryLevelsArray));
1548:
1549: length = Math.max(length, (convertTimeToBucket(oplanEndTime,
1550: false) + 1));
1551:
1552: int maxArrayLength = Math.min(criticalLevelsArray.length,
1553: inventoryLevelsArray.length);
1554:
1555: if ((length > maxArrayLength) && (logger.isWarnEnabled())) {
1556: logger
1557: .warn(getOrgName()
1558: + "-"
1559: + getItemName()
1560: + " Meaningful length is "
1561: + length
1562: + "but, criticalLevelsArray length is "
1563: + criticalLevelsArray.length
1564: + " and the inventoryLevelsArray length is "
1565: + inventoryLevelsArray.length
1566: + ". Will truncate to eliminate array index out of bounds error.");
1567: }
1568:
1569: //We want to make sure the meaningful length doesn't exceed
1570: //the length of either array.
1571: length = Math.min(length, maxArrayLength);
1572:
1573: for (int i = 0; i < length; i++) {
1574: if (criticalLevelsArray[i] >= 0.0) {
1575: list.add(ScheduleUtils.buildQuantityScheduleElement(
1576: criticalLevelsArray[i], start, start
1577: + msec_per_bucket));
1578: }
1579: start += msec_per_bucket;
1580: }
1581: bufferedCriticalLevels = GLMFactory.newQuantitySchedule(list
1582: .elements(), PlanScheduleType.OTHER);
1583: start = convertBucketToTime(0);
1584: list.clear();
1585: for (int i = 0; i < targetLevelsList.size(); i++) {
1586: Double targetLevel = (Double) targetLevelsList.get(i);
1587: if (targetLevel != null) {
1588: list.add(ScheduleUtils.buildQuantityScheduleElement(
1589: targetLevel.doubleValue(), start, start
1590: + msec_per_bucket));
1591: }
1592: start += msec_per_bucket;
1593: }
1594: bufferedTargetLevels = GLMFactory.newQuantitySchedule(list
1595: .elements(), PlanScheduleType.OTHER);
1596:
1597: start = convertBucketToTime(0);
1598: list.clear();
1599: //length =
1600: for (int i = 0; i < length; i++) {
1601: list.add(ScheduleUtils.buildQuantityScheduleElement(
1602: inventoryLevelsArray[i], start, start
1603: + msec_per_bucket));
1604: start += msec_per_bucket;
1605: }
1606: bufferedInventoryLevels = GLMFactory.newQuantitySchedule(list
1607: .elements(), PlanScheduleType.OTHER);
1608: NewScheduledContentPG scp = (NewScheduledContentPG) inventory
1609: .getScheduledContentPG();
1610: scp.setSchedule(bufferedInventoryLevels);
1611:
1612: }
1613:
1614: public boolean getFailuresFlag() {
1615: return failures;
1616: }
1617:
1618: // failures boolean used to determine when due outs
1619: // need to be re-bucketed
1620: public void setFailuresFlag(boolean value) {
1621: failures = value;
1622: }
1623:
1624: // Ask Beth about persistance. Would like to make sure structures
1625: // are persisted when they are added to the code
1626: public PGDelegate copy(PropertyGroup pg) {
1627: return new LogisticsInventoryBG((LogisticsInventoryPG) pg);
1628: }
1629:
1630: private int printQuantityScheduleTimes(Schedule sched) {
1631: Enumeration elements = sched.getAllScheduleElements();
1632: QuantityScheduleElement qse;
1633: while (elements.hasMoreElements()) {
1634: qse = (QuantityScheduleElement) elements.nextElement();
1635: if (logger.isInfoEnabled()) {
1636: logger.info("qty: " + qse.getQuantity() + " "
1637: + qse.getStartDate() + " to "
1638: + qse.getEndDate());
1639: }
1640: }
1641: return 0;
1642: }
1643:
1644: public void Test() {
1645: logger.error("Bucket size is " + msec_per_bucket
1646: / TimeUtils.MSEC_PER_HOUR + " hrs.");
1647: // logger.error("ReorderPeriod is "+reorderPeriod+", getReorderPeriod() "+getReorderPeriod()+
1648: // ", critical level "+criticalLevel);
1649: // computeCriticalLevels();
1650: // setLevel(0, myPG.getInitialLevel());
1651: // for (int i = 1; i < projectedDemandArray.length; i++) {
1652: // double new_level = getLevel(i - 1) - projectedDemandArray[i];
1653: // setLevel(i, new_level);
1654: // }
1655: // logger.error("Date for Bucket Zero is "+TimeUtils.dateString(convertBucketToTime(0)));
1656: // for (int i = 0; i < projectedDemandArray.length; i++) {
1657: // logger.error("Bucket "+i+", Demand "+getActualDemand(i)+", criticalLevel "+
1658: // criticalLevelsArray[i]+" Level "+inventoryLevelsArray[i]);
1659: // }
1660: if (logger.isErrorEnabled()) {
1661: logger.error("********* ProjectWithdrawList ********");
1662: for (int i = 0; i < projWithdrawList.size(); i++) {
1663: logger.error(taskUtils.taskDesc((Task) projWithdrawList
1664: .get(i)));
1665: }
1666: logger.error("********* WithdrawList ********");
1667: for (int i = 0; i < withdrawList.size(); i++) {
1668: logger.error(taskUtils.taskDesc((Task) withdrawList
1669: .get(i)));
1670: }
1671: logger.error("********* ProjectSupplyList ********");
1672: List flatProjList = getFlattenedList(projSupplyList);
1673: for (int i = 0; i < flatProjList.size(); i++) {
1674: logger.error(taskUtils.taskDesc((Task) flatProjList
1675: .get(i)));
1676: }
1677: logger.error("********* SupplyList ********");
1678: List flatSupplyList = getFlattenedList(supplyList);
1679: for (int i = 0; i < flatSupplyList.size(); i++) {
1680: logger.error(taskUtils.taskDesc((Task) flatSupplyList
1681: .get(i)));
1682: }
1683: logger.error("********* Buffered Critical Levels ********");
1684: printQuantityScheduleTimes(bufferedCriticalLevels);
1685: logger
1686: .error("********* Buffered Inventory Levels ********");
1687: printQuantityScheduleTimes(bufferedInventoryLevels);
1688: }
1689: }
1690:
1691: private int getMeaningfulLength(double[] list) {
1692: if (list.length < 5) {
1693: return list.length;
1694: }
1695: int lastMeaningfulPosition = list.length - 1;
1696: for (int i = list.length - 1; i > 0; i--) {
1697: if (list[i] == list[i - 1]) {
1698: lastMeaningfulPosition = i;
1699: } else {
1700: break;
1701: }
1702: }
1703: return lastMeaningfulPosition + 1;
1704: }
1705:
1706: public String getOrgName() {
1707: return myPG.getOrg().getItemIdentificationPG()
1708: .getItemIdentification();
1709: }
1710:
1711: public String getItemName() {
1712: return myPG.getResource().getTypeIdentificationPG()
1713: .getTypeIdentification();
1714: }
1715:
1716: public String getSupplyType() {
1717: SupplyClassPG pg = (SupplyClassPG) myPG.getResource()
1718: .searchForPropertyGroup(SupplyClassPG.class);
1719: return pg.getSupplyType();
1720: }
1721: }
|