0001: /*--------------------------------------------------------------------------
0002: * <copyright>
0003: *
0004: * Copyright 2000-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: package org.cougaar.glm.ldm.asset;
0027:
0028: import org.cougaar.core.mts.MessageAddress;
0029: import org.cougaar.glm.execution.common.InventoryReport;
0030: import org.cougaar.glm.ldm.Constants;
0031: import org.cougaar.glm.ldm.GLMFactory;
0032: import org.cougaar.glm.ldm.plan.AlpineAspectType;
0033: import org.cougaar.glm.ldm.plan.NewQuantityScheduleElement;
0034: import org.cougaar.glm.ldm.plan.PlanScheduleType;
0035: import org.cougaar.glm.ldm.plan.QuantityScheduleElement;
0036: import org.cougaar.glm.plugins.AssetUtils;
0037: import org.cougaar.glm.plugins.ScheduleUtils;
0038: import org.cougaar.glm.plugins.TaskUtils;
0039: import org.cougaar.glm.plugins.TimeUtils;
0040: import org.cougaar.planning.ldm.asset.Asset;
0041: import org.cougaar.planning.ldm.asset.PGDelegate;
0042: import org.cougaar.planning.ldm.asset.PropertyGroup;
0043: import org.cougaar.planning.ldm.measure.Count;
0044: import org.cougaar.planning.ldm.measure.Mass;
0045: import org.cougaar.planning.ldm.measure.Scalar;
0046: import org.cougaar.planning.ldm.measure.Volume;
0047: import org.cougaar.planning.ldm.plan.AllocationResult;
0048: import org.cougaar.planning.ldm.plan.AspectType;
0049: import org.cougaar.planning.ldm.plan.AspectValue;
0050: import org.cougaar.planning.ldm.plan.Expansion;
0051: import org.cougaar.planning.ldm.plan.PlanElement;
0052: import org.cougaar.planning.ldm.plan.Schedule;
0053: import org.cougaar.planning.ldm.plan.Task;
0054: import org.cougaar.planning.ldm.plan.Workflow;
0055: import org.cougaar.planning.plugin.util.AllocationResultHelper;
0056: import org.cougaar.util.log.Logger;
0057: import org.cougaar.util.log.Logging;
0058:
0059: import java.io.IOException;
0060: import java.io.NotActiveException;
0061: import java.io.ObjectInputStream;
0062: import java.util.ArrayList;
0063: import java.util.Calendar;
0064: import java.util.Comparator;
0065: import java.util.Date;
0066: import java.util.Enumeration;
0067: import java.util.HashMap;
0068: import java.util.Hashtable;
0069: import java.util.Iterator;
0070: import java.util.List;
0071: import java.util.Map;
0072: import java.util.Vector;
0073:
0074: public class InventoryBG implements PGDelegate {
0075:
0076: public static final int PRIORITY_LEVELS = 10;
0077: public static final long CANONICAL_TIME_OFFSET = TimeUtils.MSEC_PER_DAY - 1;
0078: public static final double GOAL_LEVEL_BOOST_CAPACITY_FACTOR = 1.1;
0079:
0080: protected InventoryPG myPG_;
0081: // Each element in these Vectors represents a day.
0082: // Element zero of each Vector is today
0083: // Refill Tasks
0084: protected transient Vector dueIns_;
0085: protected transient Vector dueInTasks_;
0086: // Supply Tasks
0087: protected transient Vector dueOut_;
0088: // ProjectSupply Tasks
0089: protected transient double[] level_;
0090: protected transient int dirtyDay_;
0091: protected transient boolean needInitializeInventoryLevels;
0092: protected transient Vector dueInsByRequestDay_;
0093: // Was the BG properly initialized, if not, it is unusable
0094: protected boolean initialized_ = false;
0095: // today is the alp day right now
0096: private boolean isStartTimeSet = false;
0097: private int startDay_;
0098: protected int today_;
0099: protected int firstDayOfDemand_;
0100: private ProjectionWeight weight_;
0101: // Stuff for execution below
0102: protected ArrayList report_history;
0103: protected InventoryReport latest_report;
0104: protected InventoryReport oldest_report;
0105: protected Calendar calendar_ = Calendar.getInstance();
0106:
0107: // target level stuff
0108: protected double[] demandSchedule_ = null;
0109: protected double goalLevelMultiplier_;
0110: protected double minReorderLevel_;
0111: protected double maxReorderLevel_;
0112: protected int daysOfDemand_;
0113: protected int daysForward_;
0114: protected int daysBackward_;
0115:
0116: protected int goalExceededCap = 0;
0117: final static protected int GOAL_EXCEEDED_CAPACITY_DEBUG_THRESHOLD = 2;
0118: private static Logger logger = Logging.getLogger(InventoryBG.class);
0119:
0120: public InventoryBG(InventoryPG pg) {
0121: myPG_ = pg;
0122: dueIns_ = new Vector();
0123: dueOut_ = new Vector();
0124: dueInsByRequestDay_ = new Vector();
0125: level_ = null;
0126: report_history = new ArrayList();
0127: latest_report = null;
0128: oldest_report = null;
0129: }
0130:
0131: public void setProjectionWeight(ProjectionWeight weight) {
0132: weight_ = weight;
0133: }
0134:
0135: // put in so that we can check whether a requisition will be counted
0136: public ProjectionWeight getProjectionWeight() {
0137: return weight_;
0138: }
0139:
0140: //Reinit transient variables from input serialization
0141: private void readObject(ObjectInputStream s) throws IOException,
0142: ClassNotFoundException, NotActiveException {
0143: s.defaultReadObject();
0144:
0145: dueIns_ = new Vector();
0146: dueOut_ = new Vector();
0147: dueInsByRequestDay_ = new Vector();
0148: level_ = null;
0149: }
0150:
0151: // GLK temp changed void to int
0152: public int resetInventory(Inventory inventory, long today) {
0153: needInitializeInventoryLevels = true;
0154:
0155: if (!isStartTimeSet) {// Only do this check once
0156: Scalar ignore = getScalar(0.0);// Insure we know how to manage this type
0157: }
0158: for (int i = 0, n = dueOut_.size(); i < n; i++) {
0159: ((DueOutList) dueOut_.elementAt(i)).clear();
0160: }
0161: for (int i = 0, n = dueIns_.size(); i < n; i++) {
0162: ((DueInList) dueIns_.elementAt(i)).clear();
0163: }
0164: dueInsByRequestDay_.clear();
0165:
0166: initializeTime(inventory, today);
0167: if (!inventory.hasScheduledContentPG()) {
0168: Asset proto = inventory.getPrototype();
0169: String id = proto.getTypeIdentificationPG()
0170: .getNomenclature();
0171: if (logger.isErrorEnabled()) {
0172: logger
0173: .error(" initInventory - NO ScheduledContentPG on Inventory Object- "
0174: + id + ", EXIT!");
0175: }
0176: System.exit(1);
0177: }
0178:
0179: // printDebug("Initializing inventory: "+inventoryDesc(inventory));
0180: // printInventory(inventory,null);
0181:
0182: // GLK temp return
0183: return 0;
0184: }
0185:
0186: /**
0187: Set the startTime to the earliest of today or the oldest
0188: inventory report. In all cases, the start time is pushed to the
0189: end of the day in which it falls.
0190: **/
0191: private void initializeTime(Inventory inventory, long today) {
0192: try {
0193: if (!isStartTimeSet) { // Need to set start time
0194: long start_time = today;
0195: InventoryReport oldest = getOldestInventoryReport();
0196: if (oldest != null) {
0197: start_time = Math.min(start_time,
0198: oldest.theReportDate);
0199: }
0200: startDay_ = (int) (start_time / TimeUtils.MSEC_PER_DAY);
0201: isStartTimeSet = true;
0202: }
0203: today_ = convertTimeToDay(today);
0204: } catch (RuntimeException re) {
0205: System.err.print(re + ": today=" + today + ", today_ = "
0206: + today_);
0207: throw re;
0208: }
0209: }
0210:
0211: public long getStartTime() {
0212: return convertDayToTime(0);
0213: }
0214:
0215: private int getStartDay() {
0216: // if (!isStartTimeSet) throw new RuntimeException("startDay_ never set");
0217: return startDay_;
0218: }
0219:
0220: public int getImputedDayOfTime(long time) {
0221: return getImputedDay(convertTimeToDay(time));
0222: }
0223:
0224: public int getImputedDay(int day) {
0225: return day - getToday();
0226: }
0227:
0228: public int getFirstPlanningDay() {
0229: // The first planning day is the first day of demand unless
0230: // that day falls in the past, in which case the first
0231: // planning day is today.
0232: return Math.max(today_ + 1, firstDayOfDemand_);
0233: }
0234:
0235: /**
0236: * @return the day number of the current execution time.
0237: **/
0238: public int getToday() {
0239: return today_;
0240: }
0241:
0242: /**
0243: * Get the inventory level at a particular execution time
0244: **/
0245: public Scalar getLevel(long time) {
0246: return getLevel(convertTimeToDay(time));
0247: }
0248:
0249: /**
0250: * Get inventory level on a particular day relative to the start
0251: * time of the inventory. If the specified day is before the start
0252: * time of the inventory, the initial level is returned. If after
0253: * the maximum day, the level on that maximum day is returned.
0254: **/
0255: public Scalar getLevel(int day) {
0256: if ((day < 0) || (level_ == null)) {
0257: return myPG_.getInitialLevel();
0258: }
0259: if (day >= level_.length) {
0260: day = level_.length - 1;
0261: }
0262: if (day >= dirtyDay_)
0263: updateLevels(day);
0264: return getScalar(level_[day]);
0265: }
0266:
0267: private void updateDirtyDay(int day) {
0268: if (day > 0 && day < dirtyDay_) {
0269: dirtyDay_ = day;
0270: }
0271: }
0272:
0273: /**
0274: * Record all demand tasks as dueouts. As a side effect compute the firstDayOfDemand_
0275: **/
0276: public int withdrawFromInventory(Inventory inventory,
0277: MessageAddress clusterID) {
0278: Enumeration role_sched = inventory.getRoleSchedule()
0279: .getRoleScheduleElements();
0280: long earliestDemand = Long.MAX_VALUE;
0281: while (role_sched.hasMoreElements()) {
0282: PlanElement pe = (PlanElement) role_sched.nextElement();
0283: Task task = pe.getTask();
0284: if (task.getVerb().equals(Constants.Verb.WITHDRAW)) {
0285: addDueOut(task);
0286: earliestDemand = Math.min(earliestDemand, TaskUtils
0287: .getEndTime(task));
0288: } else if (task.getVerb().equals(
0289: Constants.Verb.PROJECTWITHDRAW)) {
0290: addDueOutProjection(task);
0291: earliestDemand = Math.min(earliestDemand, TaskUtils
0292: .getStartTime(task));
0293: } else {
0294: System.err
0295: .println("What the .... Task added to role schedule "
0296: + TaskUtils.taskDesc(task));
0297: }
0298: }
0299: if (earliestDemand != Long.MAX_VALUE)
0300: firstDayOfDemand_ = convertTimeToDay(earliestDemand);
0301: return 0;
0302: }
0303:
0304: /**
0305: * Convert a time (long) into a day of this inventory that can be
0306: * used to index duein/out vectors, levels, etc.
0307: **/
0308: public int convertTimeToDay(long time) {
0309: int this Day = (int) (time / TimeUtils.MSEC_PER_DAY);
0310: int startDay = getStartDay();
0311: return this Day - startDay;
0312: }
0313:
0314: public long convertDayToTime(int day) {
0315: int startDay = getStartDay();
0316: return TimeUtils.MSEC_PER_DAY * (day + startDay)
0317: + CANONICAL_TIME_OFFSET;
0318: }
0319:
0320: public long getStartOfDay(int day) {
0321: int startDay = getStartDay();
0322: return TimeUtils.MSEC_PER_DAY * (day + startDay);
0323: }
0324:
0325: private long canonicalTime(long time) {
0326: return convertDayToTime(convertTimeToDay(time));
0327: }
0328:
0329: private abstract class DueIOList {
0330: List list = null; // Create on demand
0331: protected double actualTotal;
0332: protected double projectedTotal;
0333: protected int day;
0334: protected int imputedDay;
0335:
0336: protected DueIOList(int day) {
0337: this .day = day;
0338: setImputedDay();
0339: }
0340:
0341: private void setImputedDay() {
0342: imputedDay = day - today_;
0343: }
0344:
0345: public double getTotal() {
0346: return actualTotal + projectedTotal;
0347: }
0348:
0349: public double getActualTotal() {
0350: return actualTotal;
0351: }
0352:
0353: public double getProjectedTotal() {
0354: return projectedTotal;
0355: }
0356:
0357: public int size() {
0358: if (list == null)
0359: return 0;
0360: return list.size();
0361: }
0362:
0363: public void clear() {
0364: if (list != null)
0365: list.clear();
0366: if (actualTotal != 0.0 || projectedTotal != 0.0) {
0367: actualTotal = 0.0;
0368: projectedTotal = 0.0;
0369: updateDirtyDay(day);
0370: }
0371: setImputedDay();
0372: }
0373:
0374: public void add(DueIO d) {
0375: Task t = d.getTask();
0376: if (d.getFilled()) {
0377: adjustTotals(t, 1.0);
0378: updateDirtyDay(day);
0379: }
0380: if (list == null)
0381: list = new ArrayList(1);
0382: list.add(d);
0383: }
0384:
0385: public void remove(Task t) {
0386: for (int i = 0, n = list.size(); i < n; i++) {
0387: DueIO d = (DueIO) list.get(i);
0388: if (d.getTask() == t) {
0389: if (d.getFilled()) {
0390: adjustTotals(t, -1.0);
0391: updateDirtyDay(day);
0392: }
0393: list.remove(d);
0394: return;
0395: }
0396: }
0397: }
0398:
0399: protected abstract void adjustTotals(Task t, double w);
0400: }
0401:
0402: private class DueOutList extends DueIOList {
0403: public DueOutList(int day) {
0404: super (day);
0405: }
0406:
0407: public DueOut get(int ix) {
0408: return (DueOut) list.get(ix);
0409: }
0410:
0411: public List getWithdrawTasks() {
0412: if (list == null)
0413: return null;
0414: Iterator tasks = list.iterator();
0415: ArrayList actualsList = new ArrayList();
0416: Task t;
0417: while (tasks.hasNext()) {
0418: t = ((DueIO) tasks.next()).getTask();
0419: if (t.getVerb().equals(Constants.Verb.WITHDRAW)
0420: && (getWeightingFactor(t, imputedDay) > 0.0)) {
0421: actualsList.add(t);
0422: }
0423: }
0424: return actualsList;
0425: }
0426:
0427: protected void adjustTotals(Task t, double w) {
0428: double wf = getWeightingFactor(t, imputedDay) * w;
0429: if (t.getVerb().equals(Constants.Verb.WITHDRAW)) {
0430: actualTotal += TaskUtils.getWithdrawQuantity(t) * wf;
0431: } else {
0432: projectedTotal += TaskUtils.getDailyQuantity(t) * wf;
0433: }
0434: }
0435: }
0436:
0437: private class DueInList extends DueIOList {
0438: public DueInList(int day) {
0439: super (day);
0440: }
0441:
0442: public DueIn get(int ix) {
0443: return (DueIn) list.get(ix);
0444: }
0445:
0446: public List getSupplyTasks() {
0447: if (list == null)
0448: return null;
0449: Iterator tasks = list.iterator();
0450: ArrayList actualsList = new ArrayList();
0451: Task t;
0452: while (tasks.hasNext()) {
0453: t = ((DueIO) tasks.next()).getTask();
0454: if (t.getVerb().equals(Constants.Verb.SUPPLY)
0455: && (getWeightingFactor(t, imputedDay) > 0.0)) {
0456: actualsList.add(t);
0457: }
0458: }
0459: return actualsList;
0460: }
0461:
0462: protected void adjustTotals(Task t, double w) {
0463: double wf = getWeightingFactor(t, imputedDay);
0464: if (TaskUtils.isProjection(t)) {
0465: double q = TaskUtils.getDailyQuantity(t);
0466: if (q == 0.0 && w > 0.0) {
0467: if (logger.isErrorEnabled()) {
0468: logger
0469: .error("Added refill projection having 0.0 quantity "
0470: + TaskUtils.taskDesc(t));
0471: }
0472: }
0473: projectedTotal += q * w * wf;
0474: } else {
0475: if (wf < 1.0) {
0476: if (logger.isErrorEnabled()) {
0477: logger.error("Inv Day: " + day
0478: + " imputedDay: " + imputedDay
0479: + " Zero weighted supply task: "
0480: + TaskUtils.taskDesc(t));
0481: }
0482: }
0483: actualTotal += TaskUtils.getRefillQuantity(t) * w * wf;
0484: }
0485: // System.out.println("adjustTotals " + TimeUtils.dateString(convertDayToTime(day)) + ": " + getTotal());
0486: }
0487: }
0488:
0489: private void addDueOut(DueOut dueOut) {
0490: int day = dueOut.getDay();
0491: while (day >= dueOut_.size()) {
0492: dueOut_.add(new DueOutList(dueOut_.size()));
0493: }
0494: DueOutList v = (DueOutList) dueOut_.get(day);
0495: v.add(dueOut);
0496: }
0497:
0498: private void addDueOut(Task request) {
0499: int day = convertTimeToDay(TaskUtils.getEndTime(request));
0500: if (day < 0) {
0501: day = 0;
0502: if (logger.isErrorEnabled()) {
0503: logger
0504: .error("addDueOut(), Request happens in the past.(Start Time "
0505: + TimeUtils.dateString(getStartTime())
0506: + ")"
0507: + "Task time:"
0508: + TimeUtils.dateString(TaskUtils
0509: .getEndTime(request))
0510: + ", Task: "
0511: + TaskUtils.taskDesc(request));
0512: }
0513: }
0514: PlanElement pe = request.getPlanElement();
0515: // If the request has just been rescinded, the plan element will be null.
0516: // This task should not affect planning
0517: if (pe == null)
0518: return;
0519: boolean filled = true;
0520: AllocationResult ar = pe.getEstimatedResult();
0521: if (ar != null)
0522: filled = ar.isSuccess();
0523: addDueOut(new DueOut(request, filled, day));
0524: }
0525:
0526: // DRAT!!! Golden opportunity for refactoring. Emergency demo solution
0527: // AHF -- Need to merge common code from addDueOut() and addDueOutProjeciton()
0528: //
0529: private void addDueOutProjection(Task task) {
0530: PlanElement pe = task.getPlanElement();
0531: // If the task has just been rescinded, the plan element will be null.
0532: // This task should not affect planning
0533: if (pe == null)
0534: return;
0535: // Rate r = TaskUtils.getRate(task);
0536: // double rate = r.getValue(0);
0537: AllocationResultHelper helper = new AllocationResultHelper(
0538: task, task.getPlanElement());
0539: for (int i = 0, n = helper.getPhaseCount(); i < n; i++) {
0540: AllocationResultHelper.Phase phase = (AllocationResultHelper.Phase) helper
0541: .getPhase(i);
0542: double qty = phase.getAspectValue(
0543: AlpineAspectType.DEMANDRATE).getValue();
0544: boolean filled = qty > 0.0;
0545: int start = convertTimeToDay(phase.getStartTime());
0546: int end = convertTimeToDay(phase.getEndTime());
0547: if (start < 0)
0548: start = 0;
0549: for (int day = start; day < end; day++) {
0550: addDueOut(new DueOut(task, filled, day));
0551: }
0552: }
0553: }
0554:
0555: /**
0556: * Update the allocations results of all the allocations of
0557: * DueOuts for this inventory. Cycles through all the dueouts and
0558: * updates the allocation results to be success or fail on the day
0559: * of the dueout.
0560: * @return List of the changed Allocations
0561: **/
0562: public List updateDueOutAllocations() {
0563: List result = new ArrayList();
0564: int size = dueOut_.size();
0565: Map helpers = new HashMap();
0566: for (int day = 0; day < size; day++) {
0567: long startTime = getStartOfDay(day);
0568: long endTime = startTime + TimeUtils.MSEC_PER_DAY;
0569: DueOutList dueOuts = (DueOutList) dueOut_.get(day);
0570: for (int i = 0, n = dueOuts.size(); i < n; i++) {
0571: DueOut dueout = dueOuts.get(i);
0572: Task task = dueout.getTask();
0573: PlanElement pe = task.getPlanElement();
0574: if (pe == null)
0575: continue; // Probably being rescinded
0576: AllocationResultHelper helper = (AllocationResultHelper) helpers
0577: .get(pe);
0578: if (helper == null) {
0579: helper = new AllocationResultHelper(task, pe);
0580: helpers.put(pe, helper);
0581: }
0582: if (dueout.getFilled() != dueout.getPreviouslyFilled()) {
0583: if (dueout.getFilled()) {
0584: if (TaskUtils.isProjection(task)) {
0585: helper.setBest(AlpineAspectType.DEMANDRATE,
0586: startTime, endTime);
0587: } else {
0588: helper.setBest(AspectType.QUANTITY,
0589: startTime, endTime);
0590: }
0591: } else {
0592: if (TaskUtils.isProjection(task)) {
0593: helper.setFailed(
0594: AlpineAspectType.DEMANDRATE,
0595: startTime, endTime);
0596: } else {
0597: helper.setFailed(AspectType.QUANTITY,
0598: startTime, endTime);
0599: }
0600: }
0601: }
0602: }
0603: }
0604: for (Iterator i = helpers.keySet().iterator(); i.hasNext();) {
0605: PlanElement pe = (PlanElement) i.next();
0606: AllocationResultHelper helper = (AllocationResultHelper) helpers
0607: .get(pe);
0608: if (helper.isChanged()) {
0609: AllocationResult ar = helper.getAllocationResult(1.0);
0610: pe.setEstimatedResult(ar);
0611: result.add(pe);
0612: }
0613: }
0614: return result;
0615: }
0616:
0617: // Looking for the lowest priority dueout from today to the given day.
0618: public DueOut getLowestPriorityDueOutBeforeDay(int end) {
0619: DueOut lowest = null;
0620: // Also includes this day
0621: int low = Math.max(0, today_);
0622: int size = dueOut_.size() - 1;
0623: if (end > size)
0624: end = size;
0625: // Scan all dueouts for the specified days
0626: for (int i = end; i >= low; i--) {
0627: DueOutList demand = (DueOutList) dueOut_.get(i);
0628: for (int h = 0, n = demand.size(); h < n; h++) {
0629: DueOut d = demand.get(h);
0630: if (d.getFilled()) {
0631: double weight = getWeightingFactor(d.getTask(), i
0632: - today_);
0633: if (weight > 0.0) {
0634: if (lowest == null
0635: || comparePriority(d, lowest) < 0) {
0636: lowest = d;
0637: }
0638: }
0639: }
0640: }
0641: }
0642: return lowest; // Nothing found
0643: }
0644:
0645: public void setDueOutFilled(DueOut dueOut, boolean newFilled) {
0646: dueOut.setFilled(newFilled);
0647: updateDirtyDay(dueOut.getDay());
0648: }
0649:
0650: /**
0651: * Compare two DueOuts for priority.
0652: * Returns priority(d1) - priority(d2).
0653: * Remember: priority 0 is high
0654: * Remember: previouslyFilled is high
0655: * Remember: 0.0 quantity is low
0656: * @return the difference in priority of the DueOuts which will be
0657: * negative if d1 has the lowest priority.
0658: **/
0659: private int comparePriority(DueOut d1, DueOut d2) {
0660: int diff;
0661: Task t1 = d1.getTask();
0662: Task t2 = d2.getTask();
0663: diff = TaskUtils.getNumericPriority(t1)
0664: - TaskUtils.getNumericPriority(t2);
0665: if (diff != 0)
0666: return diff;
0667: diff = (d1.getPreviouslyFilled() ? 1 : 0)
0668: - (d2.getPreviouslyFilled() ? 1 : 0);
0669: if (diff != 0)
0670: return diff;
0671: diff = d2.getDay() - d1.getDay();
0672: if (diff != 0)
0673: return diff;
0674: double q1 = getRequestedDailyQuantity(t1);
0675: double q2 = getRequestedDailyQuantity(t2);
0676: if (q1 != q2)
0677: return (q1 < q2) ? -1 : 1;
0678: return 0;
0679: }
0680:
0681: private double getRequestedDailyQuantity(Task task) {
0682: if (TaskUtils.isProjection(task)) {
0683: return TaskUtils.getDailyQuantity(task);
0684: } else {
0685: return TaskUtils.getQuantity(task);
0686: }
0687: }
0688:
0689: public void computeThresholdSchedule(int daysOfDemand,
0690: int daysForward, int daysBackward, double minReorderLevel,
0691: double maxReorderLevel, double goalLevelMultiplier) {
0692:
0693: minReorderLevel_ = minReorderLevel;
0694: maxReorderLevel_ = maxReorderLevel;
0695: goalLevelMultiplier_ = goalLevelMultiplier;
0696:
0697: daysOfDemand_ = daysOfDemand;
0698: daysForward_ = daysForward;
0699: daysBackward_ = daysBackward;
0700:
0701: computeDemandSchedule();
0702:
0703: }
0704:
0705: protected double getTotalValidDueOuts(int day) {
0706: double result = Double.NaN;
0707: if (isDueOutValid(day))
0708: result = getDueOutTotal(day);
0709:
0710: if (Double.isNaN(result))
0711: return 0.0;
0712: else
0713: return result;
0714: }
0715:
0716: protected void computeDemandSchedule() {
0717: computeDemandSchedule(daysOfDemand_, daysForward_,
0718: daysBackward_);
0719: }
0720:
0721: /**
0722: * Compute a schedule of the NDaysOfDemand using and averaging
0723: * window put the results in the a schedule.
0724: */
0725:
0726: protected void computeDemandSchedule(int daysOfDemand,
0727: int daysForward, int daysBackward) {
0728:
0729: int days = level_.length;
0730:
0731: double[] newDSched = new double[days];
0732: int maxDay = dueOut_.size();
0733: int nDays = 0;
0734: double demand = 0.0;
0735: int totalPeriod = days + daysForward - 1;
0736:
0737: ///GLMDebug.DEBUG("InventoryBG::computeDemandSchedule:MWD", "MWD TESTING Start");
0738: //if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG::computeDemandSchedule:MWD", "Days Forward:" + daysForward + " Days Backward: " + daysBackward + " Days of Demand:" + daysOfDemand + " Max Day: " + maxDay);
0739:
0740: for (int i = 0; i < totalPeriod; i++) {
0741:
0742: //currDay is the day around which the window is built
0743: //i is really the leading edge of the window
0744: //start is the trailing end.
0745: int currDay = i - daysForward + 1;
0746: int start = currDay - daysBackward;
0747:
0748: double startDemand = 0.0;
0749: double endDemand = getTotalValidDueOuts(i);
0750:
0751: if (start >= 0) {
0752: startDemand = getTotalValidDueOuts(start - 1);
0753: }
0754:
0755: //If the start of the window hasn't come onto the scene
0756: //just add another day of demand
0757: if (currDay <= daysBackward) {
0758: demand += endDemand;
0759: nDays++;
0760: } else {
0761: //nominally the new day of demand comes from the right
0762: //of the window, and the 1st day of window falls off the
0763: //left
0764: demand = demand + (endDemand - startDemand);
0765: }
0766:
0767: if (currDay >= 0) {
0768: if (nDays == 0)
0769: nDays = 1;
0770: //moved daysOfDemand_ to getNDaysOfDemand
0771: newDSched[currDay] = demand / nDays;
0772: }
0773: }
0774:
0775: int len = days;
0776: if (demandSchedule_ != null) {
0777: int oldlen = demandSchedule_.length;
0778: for (int i = 0; i < len; i++) {
0779: if (i < oldlen) {
0780: double num = newDSched[i] - demandSchedule_[i];
0781: double den = newDSched[i] + demandSchedule_[i];
0782: if (den > 0.0) {
0783: if (num < 0.0 || Math.abs(num) / den < 0.1) {
0784: newDSched[i] = demandSchedule_[i];
0785: }
0786: }
0787: }
0788: }
0789: }
0790: demandSchedule_ = newDSched;
0791: }
0792:
0793: public double getGoalLevel(int day) {
0794:
0795: if (myPG_.getFillToCapacity()) {
0796: return getDouble(myPG_.getCapacity());
0797: } else {
0798: double goal_level = goalLevelMultiplier_
0799: * getReorderLevel(day);
0800: double capacity = getDouble(myPG_.getCapacity());
0801:
0802: if (goal_level > capacity) {
0803:
0804: if (goalExceededCap == GOAL_EXCEEDED_CAPACITY_DEBUG_THRESHOLD) {
0805: if (logger.isErrorEnabled()) {
0806: logger
0807: .error("getGoalLevel()::WARNING the goal level is exceeding the capacity. It is probable that you should up the capacity in the inv config file.");
0808: }
0809: }
0810:
0811: if (goalExceededCap <= GOAL_EXCEEDED_CAPACITY_DEBUG_THRESHOLD) {
0812: goalExceededCap++;
0813: }
0814: }
0815:
0816: return goal_level;
0817: }
0818: }
0819:
0820: public double getNDaysDemand(int day) {
0821: if (day >= demandSchedule_.length) {
0822: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG","ARRGH Inventory array level is : " + level_.length);
0823: throw new IllegalArgumentException(
0824: "Demand Schedule Index is wrong! index = " + day
0825: + " array is length "
0826: + demandSchedule_.length);
0827: }
0828:
0829: return (daysOfDemand_ * demandSchedule_[day]);
0830: }
0831:
0832: public double getReorderLevel(int day) {
0833:
0834: double rl = getNDaysDemand(day);
0835:
0836: if (minReorderLevel_ > maxReorderLevel_) {
0837: throw new RuntimeException(
0838: "Minimum Reorder Level is Greater than Max Reorder Level");
0839: }
0840:
0841: rl = Math.min(maxReorderLevel_,
0842: (Math.max(minReorderLevel_, rl)));
0843:
0844: return rl;
0845: }
0846:
0847: public int addPreviousRefillsToInventory(Task maintainInv) {
0848: int totalFilled = 0;
0849: if (maintainInv == null) {
0850: return 0;
0851: }
0852: PlanElement pe = maintainInv.getPlanElement();
0853: if ((pe != null) && (pe instanceof Expansion)) {
0854: Expansion expansion = (Expansion) pe;
0855: Workflow wf = expansion.getWorkflow();
0856: Enumeration tasks = wf.getTasks();
0857: while (tasks.hasMoreElements()) {
0858: Task refill = (Task) tasks.nextElement();
0859: totalFilled += addDueIn(refill);
0860: }
0861: }
0862: return totalFilled;
0863: }
0864:
0865: private void addDueIn(DueIn dueIn, int day) {
0866: if (day >= 0) {
0867: while (day >= dueIns_.size()) {
0868: dueIns_.add(new DueInList(dueIns_.size()));
0869: }
0870: DueInList v = (DueInList) dueIns_.get(day);
0871: v.add(dueIn);
0872: } else {
0873: throw new IllegalArgumentException(
0874: "InventoryBG: addDueIn(), Refill Starts in the past (start time "
0875: + TimeUtils.dateString(getStartTime())
0876: + "). Task = "
0877: + TaskUtils.taskDesc(dueIn.getTask()));
0878: }
0879: }
0880:
0881: public int addDueIn(Task refillTask) {
0882: if (TaskUtils.isProjection(refillTask)) {
0883: return addDueInProjection(refillTask);
0884: } else {
0885: return addDueInSupply(refillTask);
0886: }
0887: }
0888:
0889: private void addDueInByRequestDay(DueIn d) {
0890: int day = convertTimeToDay(TaskUtils.getEndTime(d.getTask()));
0891: while (day >= dueInsByRequestDay_.size()) {
0892: dueInsByRequestDay_.add(new Vector(1));
0893: }
0894: Vector v = (Vector) dueInsByRequestDay_.elementAt(day);
0895: v.addElement(d);
0896: }
0897:
0898: private void removeDueInByRequestDay(Task refillTask) {
0899: int day = convertTimeToDay(TaskUtils.getEndTime(refillTask));
0900: if (day < dueInsByRequestDay_.size()) {
0901: Vector v = (Vector) dueInsByRequestDay_.elementAt(day);
0902: for (Iterator i = v.iterator(); i.hasNext();) {
0903: DueIn d = (DueIn) i.next();
0904: if (d.getTask() == refillTask) {
0905: i.remove();
0906: return;
0907: }
0908: }
0909: }
0910: }
0911:
0912: private int addDueInSupply(Task refill) {
0913: int totalFilled = 0;
0914: boolean filled = true;
0915: double qty = TaskUtils.getRefillQuantity(refill);
0916: if (qty == 0.0) {
0917: filled = false;
0918: } else {
0919: filled = true;
0920: totalFilled++;
0921: }
0922: // Add Refill to daily schedule
0923: // All times set to Midnight of that day
0924: long refillTime = TaskUtils.getRefillTime(refill);
0925: int day = convertTimeToDay(refillTime);
0926: if (day >= 0) {
0927: DueIn d = new DueIn(refill, filled);
0928: addDueIn(d, day);
0929: addDueInByRequestDay(d);
0930: } else {
0931: if (logger.isErrorEnabled()) {
0932: logger.error("addDueIn() on day " + day
0933: + ", refillTime="
0934: + TimeUtils.dateString(refillTime));
0935: }
0936: }
0937: return totalFilled;
0938: }
0939:
0940: private int addDueInProjection(Task task) {
0941: int totalFilled = 0;
0942: PlanElement pe = task.getPlanElement();
0943: if (pe != null) {
0944: AllocationResultHelper helper = new AllocationResultHelper(
0945: task, pe);
0946: for (int i = 0, n = helper.getPhaseCount(); i < n; i++) {
0947: AllocationResultHelper.Phase phase = helper.getPhase(i);
0948: AspectValue av = phase
0949: .getAspectValue(AlpineAspectType.DEMANDRATE);
0950: boolean filled = av.getValue() != 0.0;
0951: totalFilled += addDueInRange(task,
0952: phase.getStartTime(), phase.getEndTime(),
0953: filled);
0954: }
0955: } else {
0956: totalFilled += addDueInRange(task, TaskUtils
0957: .getStartTime(task), TaskUtils.getEndTime(task),
0958: true);
0959: }
0960: return totalFilled;
0961: }
0962:
0963: private int addDueInRange(Task task, long startTime, long endTime,
0964: boolean filled) {
0965: int totalFilled = 0;
0966: int start = convertTimeToDay(startTime);
0967: int end = convertTimeToDay(endTime);
0968: if (start < 0) {
0969: start = 0;
0970: }
0971: for (int day = start; day < end; day++) {
0972: addDueIn(new DueIn(task, filled), day);
0973: if (filled)
0974: totalFilled++;
0975: }
0976: return totalFilled;
0977: }
0978:
0979: public int removeDueIn(Task refillTask) {
0980: // Remove Task from local structure
0981: int day = convertTimeToDay(TaskUtils.getEndTime(refillTask));
0982: try {
0983: DueInList v = (DueInList) dueIns_.get(day);
0984: v.remove(refillTask);
0985: } catch (ArrayIndexOutOfBoundsException exception) {
0986: if (logger.isErrorEnabled()) {
0987: logger.error("removeDueIn, day " + day
0988: + ": Refill not found. "
0989: + TaskUtils.taskDesc(refillTask));
0990: }
0991: }
0992: removeDueInByRequestDay(refillTask);
0993: return 0;
0994: }
0995:
0996: public int getPlanningDays() {
0997: if (level_ == null) {
0998: determineInventoryLevels();
0999: }
1000: return level_.length;
1001: }
1002:
1003: public int determineInventoryLevels() {
1004: if (needInitializeInventoryLevels) {
1005: initializeInventoryLevels();
1006: needInitializeInventoryLevels = false;
1007: }
1008: return 0;
1009: }
1010:
1011: protected void initializeInventoryLevels() {
1012: int today = getToday();
1013: int size = today + 1; // Always include tomorrow in the levels.
1014: double reported_levels[] = getTimeOrderedReportedLevels();
1015: if (dueIns_.size() > size)
1016: size = dueIns_.size();
1017: if (dueOut_.size() > size)
1018: size = dueOut_.size();
1019: if (reported_levels.length > size)
1020: size = reported_levels.length;
1021:
1022: if (level_ == null || level_.length < size) {
1023: double[] replacementLevel = new double[size];
1024:
1025: //Don't lose info that's already been computed.
1026: if (level_ != null) {
1027: System.arraycopy(level_, 0, replacementLevel, 0,
1028: level_.length);
1029: }
1030:
1031: level_ = replacementLevel;
1032: computeDemandSchedule();
1033: }
1034:
1035: /* Note that there is always at least one item in
1036: reported_levels so dirtyDay_ will always end up being
1037: greater than zero */
1038: for (int i = dirtyDay_, n = reported_levels.length; i < n; i++) {
1039: double lvl = reported_levels[i];
1040: if (!Double.isNaN(lvl)) {
1041: level_[i] = reported_levels[i];
1042: dirtyDay_ = i + 1;
1043: }
1044: }
1045: }
1046:
1047: private void updateLevels(int day) {
1048: double lvl = level_[dirtyDay_ - 1]; // dirtyDay_ always > 0 (see above)
1049: for (int i = dirtyDay_; i <= day; i++) {
1050: double dueout = getDueOutTotal(i);
1051: double duein = getDueInTotal(i);
1052: lvl += duein - dueout;
1053: level_[i] = lvl;
1054: }
1055: dirtyDay_ = day + 1;
1056: }
1057:
1058: private boolean isDueOutValid(int day) {
1059: if (day < 0)
1060: return false;
1061: if (day >= dueOut_.size())
1062: return false;
1063: return true;
1064: }
1065:
1066: private double getDueOutTotal(int day) {
1067: if (!isDueOutValid(day))
1068: return 0.0;
1069: DueOutList dueOuts = (DueOutList) dueOut_.get(day);
1070: return dueOuts.getTotal();
1071: }
1072:
1073: public Scalar getProjected(int day) {
1074: if (!isDueOutValid(day)) {
1075: return null;
1076: }
1077: DueOutList dueOuts = (DueOutList) dueOut_.get(day);
1078: return getScalar(dueOuts.getProjectedTotal());
1079: }
1080:
1081: public Scalar getProjectedRefill(int day) {
1082: if (!isDueInValid(day))
1083: return getScalar(0.0);
1084: DueInList dueIns = (DueInList) dueIns_.get(day);
1085: return getScalar(dueIns.getProjectedTotal());
1086: }
1087:
1088: private double getWeightingFactor(Task task, int imputedDay) {
1089: if (weight_ != null) {
1090: return weight_.getProjectionWeight(task, imputedDay);
1091: } else {
1092: if (logger.isErrorEnabled()) {
1093: logger
1094: .error("getWeightingFactor(), Weighting Factor NOT set.");
1095: }
1096: return 0.0;
1097: }
1098: }
1099:
1100: private boolean isDueInValid(int day) {
1101: if (day < 0)
1102: return false;
1103: if (day >= dueIns_.size())
1104: return false;
1105: return true;
1106: }
1107:
1108: private double getDueInTotal(int day) {
1109: if (!isDueInValid(day))
1110: return 0.0;
1111: DueInList dueIns = (DueInList) dueIns_.get(day);
1112: return dueIns.getTotal();
1113: }
1114:
1115: public Task refillAlreadyFailedOnDay(int day) {
1116: Enumeration dueins = null;
1117: if (day < dueInsByRequestDay_.size()) {
1118: Vector v = (Vector) dueInsByRequestDay_.get(day);
1119: dueins = v.elements();
1120: }
1121: if (dueins != null) {
1122: while (dueins.hasMoreElements()) {
1123: DueIn d = (DueIn) dueins.nextElement();
1124: Task task = d.getTask();
1125: if (task.getVerb().equals(Constants.Verb.SUPPLY)) {
1126: if (!d.getFilled())
1127: return task;
1128: }
1129: }
1130: }
1131: return null;
1132: }
1133:
1134: /**
1135: Get _the_ refill for a particular day. This assumes that only
1136: one refill exists for a particular day. In fact, we return the
1137: smallest refill for a particular day.
1138: **/
1139: public Task getRefillOnDay(int day) {
1140: Enumeration dueins = null;
1141: if (day < dueInsByRequestDay_.size()) {
1142: Vector v = (Vector) dueInsByRequestDay_.get(day);
1143: dueins = v.elements();
1144: }
1145: if (dueins != null) {
1146: Task smallestRefill = null;
1147: double smallestQuantity = Double.POSITIVE_INFINITY;
1148: while (dueins.hasMoreElements()) {
1149: DueIn d = (DueIn) dueins.nextElement();
1150: Task task = d.getTask();
1151: if (task.getVerb().equals(Constants.Verb.SUPPLY)) {
1152: double q = TaskUtils.getRefillQuantity(task);
1153: if (q < smallestQuantity) {
1154: smallestQuantity = q;
1155: smallestRefill = task;
1156: }
1157: }
1158: }
1159: return smallestRefill;
1160: }
1161: return null;
1162: }
1163:
1164: public void removeRefillProjection(int day) {
1165: if (!isDueInValid(day))
1166: return; // Nothing to remove
1167: DueInList dueIns = (DueInList) dueIns_.get(day);
1168: dueIns.clear();
1169: }
1170:
1171: public Date lastDemandTaskEnd(Inventory inventory) {
1172: long latest_time = -1;
1173: long end_time;
1174: Task task;
1175: Enumeration role_sched = inventory.getRoleSchedule()
1176: .getRoleScheduleElements();
1177: while (role_sched.hasMoreElements()) {
1178: PlanElement pe = (PlanElement) role_sched.nextElement();
1179: task = pe.getTask();
1180: latest_time = Math.max(latest_time, TaskUtils
1181: .getRefillTime(task));
1182: }
1183: if (latest_time < 0)
1184: return null;
1185: latest_time = canonicalTime(latest_time);
1186: return new Date(latest_time);
1187: }
1188:
1189: public Integer getFirstOverflow(int i, MessageAddress cluster) {
1190: if (level_ == null)
1191: determineInventoryLevels();
1192: int size = level_.length;
1193: double capacity = getDouble(myPG_.getCapacity());
1194: Integer day = null;
1195: if ((i > size) || (i < 0))
1196: return null;
1197: for (; i < size; i++) {
1198: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG()", cluster, "getFirstOverflow(), checking for overflow on "+
1199: // (TimeUtils.dateString(TimeUtils.addNDays(getStartTime(), i)))+" Level: "+level_[i]+" capacity: "+capacity);
1200: if (Math.floor(level_[i] - capacity) > 1.0) {
1201: //Put in printDebug statement because you put together the
1202: // strings whether you print them or not which can get
1203: // costly performance wise.
1204: if (logger.isDebugEnabled()) {
1205: logger.debug("getFirstOverflow(), OVERFLOW on "
1206: + (TimeUtils
1207: .dateString(convertDayToTime(i))));
1208: }
1209: return new Integer(i);
1210: }
1211: }
1212: return null;
1213: }
1214:
1215: public int updateContentSchedule(Inventory inventory) {
1216: ScheduledContentPG scp = inventory.getScheduledContentPG();
1217: QuantityScheduleElement qse;
1218: Vector new_elements = new Vector();
1219: if (level_ == null) {
1220: determineInventoryLevels();
1221: }
1222: if (level_ == null) {
1223: if (logger.isDebugEnabled()) {
1224: logger
1225: .debug("UpdateContentSchedule(), No DueOuts or DueIns, resetting schedule.");
1226: }
1227: clearContentSchedule(inventory);
1228: } else {
1229: int days = level_.length;
1230: updateLevels(days - 1); // Be sure we have all the days computed.
1231: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "UpdateContentSchedule(), Creating new Schedule for "+AssetUtils.assetDesc(inventory));
1232: for (int i = 0; i < days; i++) {
1233: // start time is eigher today or time 1st request was received + number of planning days
1234: long start = TimeUtils.addNDaysTime(getStartTime(), i);
1235: // end time is a millisecond before start time of next day
1236: long end = TimeUtils.addNDaysTime(getStartTime(),
1237: (i + 1));
1238: try {
1239: qse = ScheduleUtils.buildQuantityScheduleElement(
1240: level_[i], start, end);
1241: } catch (IllegalArgumentException iae) {
1242: iae.printStackTrace();
1243: continue;
1244: }
1245: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "UpdateContentSchedule(), Start:"+TimeUtils.dateString(start)+
1246: // ", End:"+TimeUtils.dateString(end)+", Qty: "+level_[i]);
1247: new_elements.add(qse);
1248: }
1249: }
1250: Schedule new_schedule = GLMFactory.newQuantitySchedule(
1251: new_elements.elements(),
1252: PlanScheduleType.TOTAL_INVENTORY);
1253:
1254: if (ScheduleUtils.isOffendingSchedule(new_schedule)) {
1255: if (logger.isErrorEnabled()) {
1256: logger
1257: .error("UpdateContentSchedule(), CREATED BAD SCHEDULE ");
1258: }
1259: printQuantityScheduleTimes(new_schedule);
1260: }
1261: ((NewScheduledContentPG) scp).setSchedule(new_schedule);
1262: return 0;
1263: }
1264:
1265: public int updateInventoryLevelsSchedule(Inventory inventory) {
1266: InventoryLevelsPG ilp = inventory.getInventoryLevelsPG();
1267: QuantityScheduleElement dse, rlse, glse;
1268: Vector new_av_demand_elements = new Vector();
1269: Vector new_reorder_elements = new Vector();
1270: Vector new_goal_elements = new Vector();
1271: if (demandSchedule_ == null) {
1272: if (logger.isDebugEnabled()) {
1273: logger
1274: .debug("UpdateInventoryLevelsSchedule(), No Demand Schedule Set.");
1275: }
1276: clearInventoryLevelsSchedule(inventory);
1277: } else {
1278: int days = demandSchedule_.length;
1279: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "UpdateInventoryLevelsSchedule(), Creating new Schedule for "+AssetUtils.assetDesc(inventory));
1280: for (int i = 0; i < days; i++) {
1281: // start time is eigher today or time 1st request was received + number of planning days
1282: long start = TimeUtils.addNDaysTime(getStartTime(), i);
1283: // end time is a millisecond before start time of next day
1284: long end = TimeUtils.addNDaysTime(getStartTime(),
1285: (i + 1));
1286: try {
1287: dse = ScheduleUtils.buildQuantityScheduleElement(
1288: demandSchedule_[i], start, end);
1289: rlse = ScheduleUtils.buildQuantityScheduleElement(
1290: getReorderLevel(i), start, end);
1291: glse = ScheduleUtils.buildQuantityScheduleElement(
1292: getGoalLevel(i), start, end);
1293: } catch (IllegalArgumentException iae) {
1294: iae.printStackTrace();
1295: continue;
1296: }
1297: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "UpdateInventoryLevelschedule(), Start:"+TimeUtils.dateString(start)+
1298: // ", End:"+TimeUtils.dateString(end)+", Qty: "+level_[i]);
1299: new_av_demand_elements.add(dse);
1300: new_reorder_elements.add(rlse);
1301: new_goal_elements.add(glse);
1302: }
1303: }
1304: Schedule new_av_demand_schedule = GLMFactory
1305: .newQuantitySchedule(new_av_demand_elements.elements(),
1306: PlanScheduleType.TOTAL_INVENTORY);
1307: Schedule new_reorder_schedule = GLMFactory.newQuantitySchedule(
1308: new_reorder_elements.elements(),
1309: PlanScheduleType.TOTAL_INVENTORY);
1310: Schedule new_goal_schedule = GLMFactory.newQuantitySchedule(
1311: new_goal_elements.elements(),
1312: PlanScheduleType.TOTAL_INVENTORY);
1313:
1314: if (ScheduleUtils.isOffendingSchedule(new_av_demand_schedule)) {
1315: if (logger.isErrorEnabled()) {
1316: logger
1317: .error("UpdateInventoryLevelschedule(), CREATED BAD nDays SCHEDULE ");
1318: }
1319: printQuantityScheduleTimes(new_av_demand_schedule);
1320: }
1321: if (ScheduleUtils.isOffendingSchedule(new_reorder_schedule)) {
1322: if (logger.isErrorEnabled()) {
1323: logger
1324: .error("UpdateInventoryLevelschedule(), CREATED BAD reorder SCHEDULE ");
1325: }
1326: printQuantityScheduleTimes(new_reorder_schedule);
1327: }
1328: if (ScheduleUtils.isOffendingSchedule(new_goal_schedule)) {
1329: if (logger.isErrorEnabled()) {
1330: logger
1331: .error("UpdateInventoryLevelschedule(), CREATED BAD goal SCHEDULE ");
1332: }
1333: printQuantityScheduleTimes(new_goal_schedule);
1334: }
1335: ((NewInventoryLevelsPG) ilp)
1336: .setAverageDemandSchedule(new_av_demand_schedule);
1337: ((NewInventoryLevelsPG) ilp)
1338: .setReorderLevelSchedule(new_reorder_schedule);
1339: ((NewInventoryLevelsPG) ilp)
1340: .setGoalLevelSchedule(new_goal_schedule);
1341: return 0;
1342: }
1343:
1344: public int updateDetailedContentSchedule(Inventory inventory) {
1345: if (!isStartTimeSet)
1346: return -1;
1347: if (level_ == null) {
1348: determineInventoryLevels();
1349: }
1350: if (level_ == null)
1351: return -1;
1352: // System.out.println(">>>UpdateDetailedContentSchedule ");
1353: int days = level_.length;
1354: updateLevels(days - 1); // Be sure we have all the days computed.
1355: Vector quantity_schedule_elements = new Vector();
1356: for (int i = 0; i < days; i++) {
1357: quantity_schedule_elements = createDailyScheduleElements(
1358: quantity_schedule_elements, i);
1359: }
1360: Schedule sched = GLMFactory.newQuantitySchedule(
1361: quantity_schedule_elements.elements(),
1362: PlanScheduleType.TOTAL_INVENTORY);
1363: if (ScheduleUtils.isOffendingSchedule(sched)) {
1364: if (logger.isErrorEnabled()) {
1365: logger
1366: .error("UpdateContentSchedule(), CREATED BAD SCHEDULE ");
1367: }
1368: }
1369: NewDetailedScheduledContentPG newDetailedPG = org.cougaar.glm.ldm.asset.PropertyGroupFactory
1370: .newDetailedScheduledContentPG();
1371: newDetailedPG.setSchedule(sched);
1372: inventory.setPropertyGroup(newDetailedPG);
1373: printDetailedSchedule(sched);
1374: return 0;
1375: }
1376:
1377: private Vector createDailyScheduleElements(
1378: Vector quantity_schedule_elements, int day) {
1379: long start = TimeUtils.addNDaysTime(getStartTime(), day);
1380: long end = TimeUtils.addNDaysTime(getStartTime(), day + 1);
1381: double current_level;
1382: if (day == 0) {
1383: current_level = getDouble(myPG_.getInitialLevel());
1384: } else {
1385: current_level = level_[day - 1];
1386: }
1387: ArrayList activityToday = new ArrayList();
1388: double projectedDueIns = 0;
1389: DueInList dil = null;
1390: if (dueIns_.size() > day) {
1391: dil = (DueInList) dueIns_.get(day);
1392: if ((dil != null) && (dil.size() > 0)) {
1393: activityToday.addAll(dil.getSupplyTasks());
1394: projectedDueIns = dil.getProjectedTotal();
1395: }
1396: }
1397: double projectedDueOuts = 0;
1398: DueOutList dol = null;
1399: if (dueOut_.size() > day) {
1400: dol = (DueOutList) dueOut_.get(day);
1401: if ((dol != null) && (dol.size() > 0)) {
1402: activityToday.addAll(dol.getWithdrawTasks());
1403: projectedDueOuts = dol.getProjectedTotal();
1404: }
1405: }
1406:
1407: current_level += projectedDueIns;
1408: current_level -= projectedDueOuts;
1409: quantity_schedule_elements
1410: .add(ScheduleUtils.buildQuantityScheduleElement(
1411: current_level, start, end));
1412:
1413: return accountForDailyTasks(quantity_schedule_elements,
1414: activityToday);
1415:
1416: }
1417:
1418: private Vector accountForDailyTasks(
1419: Vector quantity_schedule_elements, ArrayList activityToday) {
1420:
1421: if (!activityToday.isEmpty()) {
1422: Task[] ordered_tasks = (Task[]) activityToday
1423: .toArray(new Task[activityToday.size()]);
1424: java.util.Arrays.sort(ordered_tasks, new Comparator() {
1425: public int compare(Object o1, Object o2) {
1426: long t1_end_time = TaskUtils.getEndTime((Task) o1);
1427: long t2_end_time = TaskUtils.getEndTime((Task) o2);
1428: long diff = t1_end_time - t2_end_time;
1429: if (diff < 0L)
1430: return -1;
1431: if (diff > 0L)
1432: return 1;
1433: return 0;
1434: }
1435: });
1436: long time;
1437: int size = ordered_tasks.length;
1438: for (int k = 0; k < size; k++)
1439: System.out.println(">>> "
1440: + TaskUtils.shortTaskDesc(ordered_tasks[k]));
1441: for (int i = 0; i < size; i++) {
1442: time = TaskUtils.getEndTime(ordered_tasks[i]);
1443: QuantityScheduleElement qse = (QuantityScheduleElement) quantity_schedule_elements
1444: .get(quantity_schedule_elements.size() - 1);
1445: long previous_start = qse.getStartTime();
1446: long previous_end = qse.getEndTime();
1447: double level = qse.getQuantity();
1448: if (time > previous_start) {
1449: quantity_schedule_elements.set(
1450: quantity_schedule_elements.size() - 1,
1451: ScheduleUtils.buildQuantityScheduleElement(
1452: level, previous_start, time));
1453: if (ordered_tasks[i].getVerb().equals(
1454: Constants.Verb.SUPPLY))
1455: level += TaskUtils
1456: .getRefillQuantity(ordered_tasks[i]);
1457: else
1458: level -= TaskUtils
1459: .getWithdrawQuantity(ordered_tasks[i]);
1460: quantity_schedule_elements.add(ScheduleUtils
1461: .buildQuantityScheduleElement(level, time,
1462: previous_end));
1463: } else { // time is equal to old start time
1464: if (ordered_tasks[i].getVerb().equals(
1465: Constants.Verb.SUPPLY))
1466: level += TaskUtils
1467: .getRefillQuantity(ordered_tasks[i]);
1468: else
1469: level -= TaskUtils
1470: .getWithdrawQuantity(ordered_tasks[i]);
1471: ((NewQuantityScheduleElement) qse)
1472: .setQuantity(level);
1473: }
1474: }
1475: }
1476: return quantity_schedule_elements;
1477: }
1478:
1479: private void printDetailedSchedule(Schedule sched) {
1480: Enumeration e = sched.getAllScheduleElements();
1481: QuantityScheduleElement qse;
1482: while (e.hasMoreElements()) {
1483: qse = (QuantityScheduleElement) e.nextElement();
1484: double qty = qse.getQuantity();
1485: long start = qse.getStartTime();
1486: long end = qse.getEndTime();
1487: System.out.println(" Start: "
1488: + TimeUtils.dateString(start) + " End: "
1489: + TimeUtils.dateString(end) + " Quantity: " + qty);
1490: }
1491: }
1492:
1493: public int clearContentSchedule(Inventory inventory) {
1494: if (!isStartTimeSet)
1495: return -1;
1496: ScheduledContentPG scp = inventory.getScheduledContentPG();
1497: Schedule sched = scp.getSchedule();
1498: QuantityScheduleElement qse;
1499: Vector new_elements = new Vector();
1500: long start = TimeUtils.addNDaysTime(getStartTime(), 0);
1501: long end = TimeUtils.addNDaysTime(getStartTime(), 30);
1502:
1503: qse = ScheduleUtils.buildQuantityScheduleElement(
1504: getDouble(myPG_.getInitialLevel()), start, end);
1505: new_elements.add(qse);
1506: Schedule new_schedule = GLMFactory.newQuantitySchedule(
1507: new_elements.elements(),
1508: PlanScheduleType.TOTAL_INVENTORY);
1509:
1510: if (ScheduleUtils.isOffendingSchedule(new_schedule)) {
1511: if (logger.isErrorEnabled()) {
1512: logger
1513: .error("UpdateContentSchedule(), CREATED BAD SCHEDULE ");
1514: }
1515: printQuantityScheduleTimes(new_schedule);
1516: }
1517: ((NewScheduledContentPG) scp).setSchedule(new_schedule);
1518: return 0;
1519:
1520: }
1521:
1522: public int clearInventoryLevelsSchedule(Inventory inventory) {
1523: if (!isStartTimeSet)
1524: return -1;
1525: InventoryLevelsPG ilp = inventory.getInventoryLevelsPG();
1526: QuantityScheduleElement dse, rlse, glse;
1527: Vector new_av_demand_elements = new Vector();
1528: Vector new_reorder_elements = new Vector();
1529: Vector new_goal_elements = new Vector();
1530: long start = TimeUtils.addNDaysTime(getStartTime(), 0);
1531: long end = TimeUtils.addNDaysTime(getStartTime(), 30);
1532:
1533: dse = ScheduleUtils.buildQuantityScheduleElement(0, start, end);
1534: rlse = ScheduleUtils
1535: .buildQuantityScheduleElement(0, start, end);
1536: glse = ScheduleUtils
1537: .buildQuantityScheduleElement(0, start, end);
1538: new_av_demand_elements.add(dse);
1539: new_reorder_elements.add(rlse);
1540: new_goal_elements.add(glse);
1541:
1542: Schedule new_av_demand_schedule = GLMFactory
1543: .newQuantitySchedule(new_av_demand_elements.elements(),
1544: PlanScheduleType.TOTAL_INVENTORY);
1545: Schedule new_reorder_schedule = GLMFactory.newQuantitySchedule(
1546: new_reorder_elements.elements(),
1547: PlanScheduleType.TOTAL_INVENTORY);
1548: Schedule new_goal_schedule = GLMFactory.newQuantitySchedule(
1549: new_goal_elements.elements(),
1550: PlanScheduleType.TOTAL_INVENTORY);
1551:
1552: if (ScheduleUtils.isOffendingSchedule(new_av_demand_schedule)) {
1553: if (logger.isErrorEnabled()) {
1554: logger
1555: .error("clearInventoryLevelSchedule(), CREATED BAD nDays SCHEDULE ");
1556: }
1557: printQuantityScheduleTimes(new_av_demand_schedule);
1558: }
1559: if (ScheduleUtils.isOffendingSchedule(new_reorder_schedule)) {
1560: if (logger.isErrorEnabled()) {
1561: logger
1562: .error("clearInventoryLevelSchedule(), CREATED BAD reorder SCHEDULE ");
1563: }
1564: printQuantityScheduleTimes(new_reorder_schedule);
1565: }
1566: if (ScheduleUtils.isOffendingSchedule(new_goal_schedule)) {
1567: if (logger.isErrorEnabled()) {
1568: logger
1569: .error("clearInventoryLevelSchedule(), CREATED BAD goal SCHEDULE ");
1570: }
1571: printQuantityScheduleTimes(new_goal_schedule);
1572: }
1573: ((NewInventoryLevelsPG) ilp)
1574: .setAverageDemandSchedule(new_av_demand_schedule);
1575: ((NewInventoryLevelsPG) ilp)
1576: .setReorderLevelSchedule(new_reorder_schedule);
1577: ((NewInventoryLevelsPG) ilp)
1578: .setGoalLevelSchedule(new_goal_schedule);
1579:
1580: return 0;
1581:
1582: }
1583:
1584: public int printQuantityScheduleTimes(Schedule sched) {
1585: Enumeration elements = sched.getAllScheduleElements();
1586: QuantityScheduleElement qse;
1587:
1588: while (elements.hasMoreElements()) {
1589: qse = (QuantityScheduleElement) elements.nextElement();
1590: if (logger.isDebugEnabled()) {
1591: logger.debug("printQuantityScheduleTimes() qty: "
1592: + qse.getQuantity() + " " + qse.getStartTime()
1593: + " to " + qse.getEndTime());
1594: }
1595: }
1596: return 0;
1597: }
1598:
1599: public PGDelegate copy(PropertyGroup pg) {
1600: return new InventoryBG((InventoryPG) pg);
1601: }
1602:
1603: /**
1604: * Add the newInventoryReport to the report_history ArrayList
1605: **/
1606: public void addInventoryReport(InventoryReport newInventoryReport) {
1607: synchronized (this ) {
1608: report_history.add(newInventoryReport);
1609: if (latest_report == null
1610: || latest_report.theReportDate < newInventoryReport.theReportDate) {
1611: latest_report = newInventoryReport;
1612: }
1613: if (oldest_report == null
1614: || oldest_report.theReportDate > newInventoryReport.theReportDate) {
1615: oldest_report = newInventoryReport;
1616: }
1617: }
1618: if (logger.isDebugEnabled()) {
1619: logger.debug("addInventoryReport(), Report added!!!!!!! : "
1620: + this .hashCode());
1621: }
1622: }
1623:
1624: /**
1625: * Remove inventory reports older than a specified time. Never removes the latest report.
1626: **/
1627: public void pruneOldInventoryReports(long pruneTime) {
1628: synchronized (this ) {
1629: for (Iterator i = report_history.iterator(); i.hasNext();) {
1630: InventoryReport report = (InventoryReport) i.next();
1631: if (pruneTime > report.theReportDate
1632: && report != latest_report) {
1633: i.remove();
1634: }
1635: }
1636: oldest_report = null; // We probably removed this, recompute when asked
1637: }
1638: }
1639:
1640: /**
1641: * Returns the report_history ArrayList
1642: **/
1643: private ArrayList getInventoryReportHistory() {
1644: synchronized (this ) {
1645: return report_history;
1646: }
1647: }
1648:
1649: /**
1650: * Returns the InventoryReport with the latest date
1651: **/
1652: public InventoryReport getLatestInventoryReport() {
1653: synchronized (this ) {
1654: if (latest_report == null) { // This is almost always up-to-date, but in case it isn't...
1655: for (Iterator i = report_history.iterator(); i
1656: .hasNext();) {
1657: InventoryReport report = (InventoryReport) i.next();
1658: if (latest_report == null
1659: || latest_report.theReportDate < report.theReportDate) {
1660: latest_report = report;
1661: }
1662: }
1663: }
1664: return latest_report;
1665: }
1666: }
1667:
1668: /**
1669: * Returns the InventoryReport with the oldest date
1670: **/
1671: public InventoryReport getOldestInventoryReport() {
1672: synchronized (this ) {
1673: if (oldest_report == null) { // This is almost always up-to-date, but in case it isn't...
1674: for (Iterator i = report_history.iterator(); i
1675: .hasNext();) {
1676: InventoryReport report = (InventoryReport) i.next();
1677: if (oldest_report == null
1678: || oldest_report.theReportDate > report.theReportDate) {
1679: oldest_report = report;
1680: }
1681: }
1682: }
1683: return oldest_report;
1684: }
1685: }
1686:
1687: /**
1688: * Returns a time-ordered list of known InventoryReports
1689: **/
1690: private double[] getTimeOrderedReportedLevels() {
1691: double ordered_list[];
1692: InventoryReport[] reports;
1693: synchronized (this ) { // Must be synchronized so reports cannot change while we process them
1694: List list = getInventoryReportHistory();
1695: int size = list.size();
1696: reports = (InventoryReport[]) list
1697: .toArray(new InventoryReport[size]);
1698: }
1699: // No Reports - use PG for initial inventory level
1700: if (reports.length == 0) {
1701: ordered_list = new double[1];
1702: ordered_list[0] = getDouble(myPG_.getInitialLevel());
1703: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "getTimeOrderedReportedLevels(), List is empty.");
1704: } else {
1705: // if(GLMDebug.printDebug()) GLMDebug.DEBUG("InventoryBG", "getTimeOrderedReportedLevels(), Sorting Report list.");
1706: // Sort the Reports by time in ascending order
1707: // Yah, it's inefficient but it's only a few lines of code and there should never
1708: // be more than 5 items in this list
1709: java.util.Arrays.sort(reports, new Comparator() {
1710: public int compare(Object o1, Object o2) {
1711: InventoryReport r1 = (InventoryReport) o1;
1712: InventoryReport r2 = (InventoryReport) o2;
1713: long diff = r1.theReportDate - r2.theReportDate;
1714: if (diff < 0L)
1715: return -1;
1716: if (diff > 0L)
1717: return 1;
1718: return 0;
1719: }
1720: });
1721: long latest = reports[reports.length - 1].theReportDate;
1722:
1723: // create a double[] containing inventory levels indexed by day
1724: int reportDay = convertTimeToDay(latest);
1725: int len = reportDay + 1;
1726: if (len <= 0) {
1727: len = 1;
1728: }
1729: ordered_list = new double[len];
1730: java.util.Arrays.fill(ordered_list, Double.NaN);
1731: ordered_list[0] = getDouble(myPG_.getInitialLevel());
1732: for (int i = 0, n = reports.length; i < n; i++) {
1733: long report_date = reports[i].theReportDate;
1734: int day = convertTimeToDay(report_date);
1735: // Last Report that was received before or on day 0 becomes initial value
1736: if (day < 0) {
1737: day = 0;
1738: }
1739: ordered_list[day] = reports[i].theQuantity;
1740: if (logger.isDebugEnabled()) {
1741: logger
1742: .debug("getTimeOrderedReportedLevels(), Report day is "
1743: + day
1744: + "("
1745: + TimeUtils.dateString(report_date)
1746: + "), Value is: "
1747: + ordered_list[day]);
1748: }
1749: }
1750: }
1751: return ordered_list;
1752: }
1753:
1754: private static final Volume zeroVolume = new Volume(0,
1755: Volume.GALLONS);
1756: private static final Count zeroCount = new Count(0, Count.EACHES);
1757: private static final Mass zeroMass = new Mass(0, Mass.SHORT_TONS);
1758:
1759: /**
1760: * Given a double, create scalar apropriate for this type of inventory
1761: **/
1762: private Scalar getScalar(double amt) {
1763: Scalar measure = myPG_.getInitialLevel();
1764: if (measure instanceof Volume) {
1765: if (amt == 0.0)
1766: return zeroVolume;
1767: else
1768: return new Volume(amt, Volume.GALLONS);
1769: }
1770: if (measure instanceof Count) {
1771: if (amt == 0.0)
1772: return zeroCount;
1773: else
1774: return new Count(amt, Count.EACHES);
1775: }
1776: if (measure instanceof Mass) {
1777: if (amt == 0.0)
1778: return zeroMass;
1779: else
1780: return new Mass(amt, Mass.SHORT_TONS);
1781: }
1782: throw new RuntimeException(
1783: "InventoryBG: Unknown type of initial level");
1784: }
1785:
1786: /**
1787: * Given a Scalar, return a double value representing
1788: * Gallons for Volume,
1789: * Eaches for Count and
1790: * Short Tons for Mass.
1791: **/
1792: private double getDouble(Scalar measure) {
1793: double result = Double.NaN;
1794: if (measure instanceof Volume) {
1795: result = ((Volume) measure).getGallons();
1796: } else if (measure instanceof Count) {
1797: result = ((Count) measure).getEaches();
1798: } else if (measure instanceof Mass) {
1799: result = ((Mass) measure).getShortTons();
1800: } else {
1801: if (logger.isErrorEnabled()) {
1802: logger
1803: .error("getDouble(), Inventory cannot determine type of measure");
1804: }
1805: }
1806: return result;
1807: }
1808:
1809: public Enumeration getAllDueIns() {
1810: Hashtable dueInTaskHT = new Hashtable(500);
1811: dueInTasks_ = new Vector(500);
1812: int size = dueIns_.size();
1813: Vector div;
1814: for (int i = 0; i < size; i++) {
1815: div = (Vector) (dueIns_.get(i));
1816: for (int j = 0; j < div.size(); j++) {
1817: if (dueInTaskHT.get(((DueIn) div.get(j)).getTask()) == null) {
1818: dueInTaskHT.put(((DueIn) div.get(j)).getTask(),
1819: ((DueIn) div.get(j)));
1820: dueInTasks_.add(((DueIn) div.get(j)).getTask()
1821: .getPlanElement());
1822: }
1823: }
1824: }
1825: return dueInTasks_.elements();
1826: }
1827:
1828: public int printInventoryLevels(Inventory inventory,
1829: MessageAddress clusterID) {
1830: if (logger.isDebugEnabled()) {
1831: logger.debug("printInventoryLevels(), Day 0 is "
1832: + TimeUtils.dateString(getStartTime()));
1833: }
1834: if (level_ != null) {
1835: int size = level_.length;
1836: ScheduledContentPG scpg = inventory.getScheduledContentPG();
1837: if (logger.isDebugEnabled()) {
1838: logger.debug("printInventoryLevels(), for "
1839: + AssetUtils.assetDesc(scpg.getAsset()));
1840: }
1841:
1842: for (int i = 0; i < size; i++) {
1843: if (logger.isDebugEnabled()) {
1844: logger.debug("printInventoryLevels(), day: "
1845: + TimeUtils.dateString(TimeUtils.addNDays(
1846: getStartTime(), i)) + "(" + i
1847: + ") " + ", level: " + level_[i]);
1848: }
1849: }
1850: }
1851: return 0;
1852: }
1853: }
|