001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.logistics.plugin.inventory;
028:
029: import org.cougaar.glm.ldm.plan.AlpineAspectType;
030: import org.cougaar.logistics.ldm.Constants;
031: import org.cougaar.glm.ldm.asset.Inventory;
032: import org.cougaar.planning.ldm.plan.AllocationResult;
033: import org.cougaar.planning.ldm.plan.AspectType;
034: import org.cougaar.planning.ldm.plan.AspectValue;
035: import org.cougaar.planning.ldm.plan.PlanElement;
036: import org.cougaar.planning.ldm.plan.Task;
037:
038: import java.util.*;
039:
040: public class InventoryLevelGenerator extends InventoryModule {
041:
042: public InventoryLevelGenerator(InventoryManager imPlugin) {
043: super (imPlugin);
044: }
045:
046: public void calculateInventoryLevels(int startBucket,
047: int endBucket, LogisticsInventoryPG thePG) {
048: //calculate inventory levels for today through start (today + OST)
049: int start = (thePG.getStartBucket() > startBucket) ? thePG
050: .getStartBucket() : startBucket;
051: while (start <= endBucket) {
052: double level;
053: if (start == 0) {
054: level = thePG.getLevel(0);
055: } else {
056: level = thePG.getLevel(start - 1)
057: - thePG.getActualDemand(start);
058: }
059: double committedRefill = findCommittedRefill(start, thePG,
060: true);
061: thePG.setLevel(start, (level + committedRefill));
062: start = start + 1;
063: }
064:
065: }
066:
067: /** Utility method to help find commited refills
068: * NOTE this only finds a quantity IF there is a reported or
069: * Estimated AllocationResult for the Task!
070: * @param bucket The time bucket to match the Task with
071: * @param thePG The PG for the Inventory the Tasks are against
072: * @param countProjections Count projection refill results
073: * @return double The quantity of the committed Refill Task for the time period.
074: **/
075: protected double findCommittedRefill(int bucket,
076: LogisticsInventoryPG thePG, boolean countProjections) {
077:
078: double refillQty = 0;
079: ArrayList reqs = thePG.getRefillRequisitions();
080: ArrayList refills = null;
081: Task refill = null;
082: if (bucket < reqs.size()) {
083: refills = (ArrayList) reqs.get(bucket);
084: }
085:
086: // long today = inventoryPlugin.getCurrentTimeMillis();
087: // // max lead day is today + maxLeadTime
088: // int maxLeadBucket = thePG.convertTimeToBucket(getTimeUtils().
089: // addNDays(today, inventoryPlugin.getMaxLeadTime()), false);
090: // if ((refill == null) && (bucket > maxLeadBucket)) {
091: // refill = thePG.getRefillProjection(bucket);
092: // }
093: // check that the bucket we're looking at is in the projection period and its
094: // not just an off day during the Requisition period
095: int lastReqBucket = thePG.getLastRefillRequisition();
096: if (((refills == null) || (refills.size() == 0))
097: && (bucket > lastReqBucket) && (countProjections)) {
098: refills = thePG.getRefillProjection(bucket);
099: }
100:
101: //!!!NOTE that the inside slots of thePG.getRefillRequisitions are sometimes
102: // filled with null instead of a task - so make sure you really have a task!
103: if (refills != null) {
104: for (int i = 0; i < refills.size(); i++) {
105: refill = (Task) refills.get(i);
106: PlanElement pe = refill.getPlanElement();
107: AllocationResult ar = null;
108: if (pe != null) {
109: //try to use the reported result - but if its null - use the
110: // estimated result
111: if (pe.getReportedResult() != null) {
112: ar = pe.getReportedResult();
113: } else {
114: ar = pe.getEstimatedResult();
115: }
116: // make sure that we got at least a valid reported OR estimated allocation result
117: //if (ar != null) {
118: if (ar != null && ar.isSuccess()) {
119: if (refill.getVerb().equals(
120: Constants.Verb.PROJECTSUPPLY)) {
121: //demandrate
122: // if (inventoryPlugin.getSupplyType().equals("BulkPOL")) {
123: // //default rate for volume is days
124: // refillQty = ar.getValue(AlpineAspectType.DEMANDRATE);
125: // } else {
126: // //default rate for counts is millis
127: // refillQty = ar.getValue(AlpineAspectType.DEMANDRATE) * thePG.getBucketMillis();
128: // }
129: refillQty += getTaskUtils()
130: .getQuantity(refill, ar,
131: thePG.getBucketMillis());
132: } else {
133: /* We may have the same task in multiple buckets. We need to go through all the
134: phasedAllocationResults for a given task and find the one that matches the time of this bucket.
135: A task will have multiple phases, only one of which will apply to the bucket
136: that we are currently processing.
137: */
138: if (ar.isPhased()) {
139: int[] ats = ar.getAspectTypes();
140: int endInd = getIndexForType(ats,
141: AspectType.END_TIME);
142: Enumeration phasedResults = ar
143: .getPhasedResults();
144: while (phasedResults.hasMoreElements()) {
145: double[] results = (double[]) phasedResults
146: .nextElement();
147: long phasedEndTime;
148: phasedEndTime = (long) results[endInd];
149: long bucketStartTime = thePG
150: .convertBucketToTime(bucket);
151: long bucketEndTime = bucketStartTime
152: + (thePG.getBucketMillis() - 1);
153: if ((phasedEndTime >= bucketStartTime)
154: && (phasedEndTime <= bucketEndTime)) {
155: //increment the quantity
156: int quantInd = getIndexForType(
157: ats,
158: AspectType.QUANTITY);
159: refillQty += results[quantInd];
160: }
161: }
162:
163: } else {
164: try {
165: refillQty += ar
166: .getValue(AspectType.QUANTITY);
167: } catch (IllegalArgumentException iae) {
168: if (logger.isErrorEnabled()) {
169: logger
170: .error("findCommittedRefill - The refill task "
171: + refill
172: .getUID()
173: + "'s plan element has an allocation result without a quantity : "
174: + ar);
175: }
176: } catch (Exception e) {
177: if (ar == null
178: && logger.isErrorEnabled()) {
179: logger
180: .error("This would blow our minds.");
181: } else {
182: if (logger.isErrorEnabled()) {
183: logger
184: .error(" Task is "
185: + getTaskUtils()
186: .taskDesc(
187: refill));
188: e.printStackTrace();
189: }
190: }
191: }
192: }
193: }
194: }
195: }
196: }
197: }
198: // if we did not find a match return 0.0
199: return refillQty;
200: }
201:
202: public static int getIndexForType(int[] types, int type) {
203: for (int ii = 0; ii < types.length; ii++) {
204: if (types[ii] == type) {
205: return ii;
206: }
207: }
208: return -1;
209: }
210:
211: /** Determines and sets the Target and Inventory Levels for projection period.
212: * Target and Inventory Levels are used for display purposes only.
213: * The calculation for Target Level during the projection period is:
214: * target = (criticalLevelEnd - criticalLevelBegin) + demand
215: * The calculation for the Inventory level for each bucket during the projection period is
216: * the average of the critical level and the target level for that day.
217: * Note that since target levels are only set for each reorder period which usually spans
218: * more than one bucket, we must interpolate what the target level would be for buckets in
219: * the middle of the reorder period.
220: * @param thePG The LogisticsInventoryPG for current inventory
221: * @param startBucket The bucket that starts the Projection period.
222: **/
223: protected void setTargetForProjectionPeriod(
224: LogisticsInventoryPG thePG, int startBucket,
225: double prevTarget) {
226: if (logger.isDebugEnabled()) {
227: logger
228: .debug("For item: "
229: + thePG.getResource()
230: + " set Projection inventory and target levels starting with bucket: "
231: + startBucket);
232: }
233:
234: // Bug #13358 clearTargetLevels needs to be called for both refill period and projection
235: // period. TargetLevels in the refill period are cleared from firstLegalRefillBucket
236: // forward but when no refills are generated it may be the case that firstLegalRefillBucket
237: // is greater than startBucket for projection period, thereby not properly
238: // clearing target levels which can result in a 'jagged' target level.
239: thePG.clearTargetLevels(startBucket);
240:
241: int reorderPeriod = (int) thePG.getReorderPeriod();
242: int lastDemandBucket = thePG.getLastDemandBucket();
243: double lastTarget;
244: int inventoryBucket, inventoryBucketStart;
245:
246: // get the first target before we loop so we have 2 points for the
247: // inventory level calculations.
248: int reorderPeriodEndBucket = startBucket + reorderPeriod;
249: double target = getTargetLevel(startBucket,
250: reorderPeriodEndBucket, thePG);
251: thePG.setTarget(startBucket, target);
252: lastTarget = target;
253: // System.out.println("Last demand bucket for "+inventoryPlugin.getResourceName(thePG.getResource())+
254: // "-"+getAssetUtils().getPartNomenclature(thePG.getResource())+" is "+
255: // lastDemandBucket+" at "+getOrgName());
256: // Start the loop after the first reorderperiod
257: for (int targetLevelBucket = startBucket + reorderPeriod; targetLevelBucket <= lastDemandBucket; targetLevelBucket = targetLevelBucket
258: + reorderPeriod) {
259: reorderPeriodEndBucket = targetLevelBucket + reorderPeriod;
260: target = getTargetLevel(targetLevelBucket,
261: reorderPeriodEndBucket, thePG);
262: thePG.setTarget(targetLevelBucket, target);
263:
264: // set the start point for the inventory calcualtions to the
265: // beginning of the last target window
266: //ie whatever bucket lastTarget was set for.
267: inventoryBucketStart = targetLevelBucket - reorderPeriod;
268:
269: for (int i = 0; i < reorderPeriod; i++) {
270: inventoryBucket = inventoryBucketStart + i;
271: double diff = target - lastTarget;
272: double calcTarget = lastTarget
273: + ((diff * i) / reorderPeriod);
274: //inv level in projection land is the average of the critcial and target levels
275: double level = (thePG.getCriticalLevel(inventoryBucket) + calcTarget) / 2;
276:
277: //Only for Level2Projections do we call this method with a startBucket == 0
278: //For Level2Projections we were resetting the zero th bucket of invnentory
279: //which is really what holds the initial inventory level -
280: //Don't reset the initial inventory level!
281: if (inventoryBucket > 0) {
282: thePG.setLevel(inventoryBucket, level);
283: }
284: }
285: lastTarget = target;
286: }
287: }
288:
289: /** Utility method to generate the Refill Amount
290: * This method starts the following calculation
291: * RF(k+1)=(C(k+RP+1)-IL(k+1)) + (D(K+2)+...+(k+RP+1))
292: * @param refillBucket The bucket we are generating a refill for. This is
293: * the bucket at k+1
294: * @param reorderPeriodEndBucket The end point for which we want demand for.
295: * This is the bucket at k+RP+1.
296: * @param thePG The LogisticsInventoryPG of the Inventory we are Refilling.
297: * @return double The Refill Amount. This is RF(k+1)
298: **/
299: protected double getTargetLevel(int refillBucket,
300: int reorderPeriodEndBucket, LogisticsInventoryPG thePG) {
301: double targetLevel = 0;
302: // double criticalAtEndOfPeriod = Math.max(.9, thePG.getCriticalLevel(reorderPeriodEndBucket));
303: double criticalAtEndOfPeriod = thePG
304: .getCriticalLevel(reorderPeriodEndBucket);
305: double demandForPeriod = calculateDemandForPeriod(thePG,
306: refillBucket, reorderPeriodEndBucket);
307: // targetLevel = criticalAtEndOfPeriod + demandForPeriod;
308: // used once to fix a problem with fresh fruit - but seems wrong since the rest of the
309: // refill generator algorithm expects us to calculate for the whole reorder period
310: // switched back to original (above) for 10.4
311: // double demandForPeriod = calculateDemandForPeriod(thePG,
312: // refillBucket,
313: // reorderPeriodEndBucket-1);
314:
315: targetLevel = criticalAtEndOfPeriod + demandForPeriod + .0005;
316: if (logger.isDebugEnabled()) {
317: if ((inventoryPlugin.getOrgName().indexOf("ARBN") > -1)
318: && (thePG.getResource().getTypeIdentificationPG()
319: .getTypeIdentification().indexOf("C380") > -1)) {
320: logger
321: .debug("##ILG## "
322: + getTimeUtils()
323: .dateString(
324: thePG
325: .convertBucketToTime(refillBucket))
326: + " Target " + targetLevel
327: + " = critical "
328: + criticalAtEndOfPeriod + " + demand "
329: + demandForPeriod);
330: }
331: }
332: return targetLevel;
333: }
334:
335: /** Utility method to calculate the demand for the Reorder Period
336: * This method does the calculation for:
337: * (D(K+2)+...+(k+RP+1))
338: * @param thePG The LogisticsInventoryPG of the Inventory we are Refilling.
339: * @param refillBucket The bucket we are generating a refill for. This is
340: * the bucket at k+1
341: * This is the bucket at k+RP+1.
342: * @return double The sum of Demand for the Reorder Period.
343: **/
344: protected double calculateDemandForPeriod(
345: LogisticsInventoryPG thePG, int refillBucket,
346: int endOfPeriodBucket) {
347: double totalDemand = 0.0;
348: int currentBucket = refillBucket + 1;
349: while (currentBucket <= endOfPeriodBucket) {
350: double demand = thePG.getActualDemand(currentBucket);
351: totalDemand = totalDemand + demand;
352: currentBucket = currentBucket + 1;
353: }
354: return totalDemand;
355: }
356: }
|