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.trans;
028:
029: import java.util.*;
030:
031: import org.cougaar.planning.ldm.plan.*;
032: import org.cougaar.planning.ldm.asset.Asset;
033: import org.cougaar.planning.ldm.asset.TypeIdentificationPG;
034: import org.cougaar.planning.ldm.asset.ItemIdentificationPG;
035: import org.cougaar.logistics.ldm.Constants;
036: import org.cougaar.glm.ldm.asset.Organization;
037: import org.cougaar.glm.ldm.plan.GeolocLocation;
038: import org.cougaar.glm.ldm.plan.AlpineAspectType;
039: import org.cougaar.planning.ldm.plan.AspectValue;
040: import org.cougaar.planning.ldm.measure.*;
041: import org.cougaar.logistics.plugin.inventory.MaintainedItem;
042: import org.cougaar.logistics.plugin.inventory.TaskUtils;
043: import org.cougaar.logistics.plugin.inventory.TimeUtils;
044: import org.cougaar.util.Random;
045:
046: /**
047: * The Level2Expander expands a Level2Task into it's adjusted
048: * (by Level6 tasks) equivilent
049: *
050: * @see Level2TranslatorPlugin
051: * @see Level2TranslatorModule
052: **/
053:
054: public class Level2Expander extends Level2TranslatorModule {
055:
056: HashMap level2To6Map;
057: HashMap endTimeMap;
058:
059: private static AllocationResultAggregator expansionARA = new ExpansionARA();
060:
061: public Level2Expander(Level2TranslatorPlugin level2Translator) {
062: super (level2Translator);
063: level2To6Map = new HashMap();
064: endTimeMap = new HashMap();
065: }
066:
067: public Collection translateLevel2Tasks(Collection level2Tasks,
068: Collection level6Tasks, Collection supplyTasks) {
069: ArrayList doneLevel2s = new ArrayList();
070: mapLevel2ToLevel6(level2Tasks, level6Tasks);
071: mapCustomerEndTimes(supplyTasks);
072: Iterator subIt = level2To6Map.entrySet().iterator();
073: while (subIt.hasNext()) {
074: Map.Entry entry = (Map.Entry) subIt.next();
075: Task level2Task = (Task) entry.getKey();
076: Collection relLevel6Tasks = (Collection) entry.getValue();
077: Task aDoneLevel2 = translateLevel2Task(level2Task,
078: relLevel6Tasks);
079: if (aDoneLevel2 != null) {
080: doneLevel2s.add(aDoneLevel2);
081: }
082: }
083:
084: return doneLevel2s;
085: }
086:
087: private Task translateLevel2Task(Task level2Task,
088: Collection relevantL6Tasks) {
089: Task doneLevel2Task = null;
090: long endTime = getTaskUtils().getEndTime(level2Task);
091: long startTime = getTaskUtils().getStartTime(level2Task);
092: long earliestLegalStart = startTime;
093: Long custLastActual = (Long) endTimeMap.get(getTaskUtils()
094: .getCustomer(level2Task));
095: if (custLastActual != null) {
096: earliestLegalStart = custLastActual.longValue()
097: + getTimeUtils().MSEC_PER_DAY;
098: }
099: boolean alreadyDisposed = false;
100:
101: double expL2BaseRate = 0;
102: PlanElement pe = level2Task.getPlanElement();
103: if (pe != null) {
104: if (pe instanceof Expansion) {
105: Expansion exp = (Expansion) pe;
106: Enumeration subtasks = exp.getWorkflow().getTasks();
107: while (subtasks.hasMoreElements()) {
108: Task subtask = (Task) subtasks.nextElement();
109: expL2BaseRate += getBaseUnitPerSecond(getTaskUtils()
110: .getRate(subtask));
111: }
112: } else if (pe instanceof Disposition) {
113: alreadyDisposed = true;
114: }
115: }
116:
117: if (earliestLegalStart >= endTime) {
118: doneLevel2Task = level2Task;
119: } else {
120: long countedStartTime = Math.max(startTime,
121: earliestLegalStart);
122: double totalL6BaseQty = deriveTotalQty(countedStartTime,
123: endTime, relevantL6Tasks);
124: Rate origL2Rate = getTaskUtils().getRate(level2Task);
125: double origL2BaseQty = deriveTotalQty(countedStartTime,
126: endTime, level2Task);
127: //
128:
129: double toleranceFactor = 0.10; // for ammo within 200 lbs
130:
131: if ((origL2BaseQty - totalL6BaseQty) < toleranceFactor) {
132: if (((totalL6BaseQty - origL2BaseQty) > 2.0)
133: && logger.isDebugEnabled()) {
134: logger
135: .debug("level2Task "
136: + level2Task
137: + " has a total qty of "
138: + origL2BaseQty
139: + " which is exceeded by the level 6 tasks with a summed total qty of "
140: + totalL6BaseQty);
141: }
142: //If already disposed we still want to redispose in case there has been a qty change
143: doneLevel2Task = level2Task;
144: } else {
145: double durationMillis = (endTime - countedStartTime);
146: double durationSecs = (durationMillis / 1000);
147: double newL2BaseRate = ((origL2BaseQty - totalL6BaseQty) / durationSecs);
148: if (newL2BaseRate != expL2BaseRate) {
149: double origL2BaseRate = getBaseUnitPerSecond(origL2Rate);
150: if (logger.isDebugEnabled()) {
151: logger
152: .debug("Level2Expander:Original Rate per day:"
153: + (origL2BaseRate * TimeUtils.SEC_PER_DAY)
154: + ". Changing expanded rate from "
155: + (expL2BaseRate * TimeUtils.SEC_PER_DAY)
156: + " to "
157: + (newL2BaseRate * TimeUtils.SEC_PER_DAY));
158: logger
159: .debug(" and L2 Start time is "
160: + new Date(startTime)
161: + " and end time is "
162: + new Date(endTime)
163: + ", earlies legal (lastActual + 1 day) is "
164: + new Date(earliestLegalStart));
165: logger.debug(" and also origL2BaseQty:"
166: + origL2BaseQty + " totalL6BaseQty:"
167: + totalL6BaseQty);
168: }
169:
170: Rate newL2Rate = newRateFromUnitPerSecond(
171: origL2Rate, newL2BaseRate);
172: expandLevel2Task(level2Task, newL2Rate,
173: earliestLegalStart);
174: }
175: }
176: }
177:
178: return doneLevel2Task;
179: }
180:
181: private void mapCustomerEndTimes(Collection supplyTasks) {
182: endTimeMap.clear();
183: Iterator supplyTaskIt = supplyTasks.iterator();
184: while (supplyTaskIt.hasNext()) {
185: Task supplyTask = (Task) supplyTaskIt.next();
186: long endTime = getTaskUtils().getEndTime(supplyTask);
187: Object org = getTaskUtils().getCustomer(supplyTask);
188: if (org != null) {
189: Long lastActualSeen = (Long) endTimeMap.get(org);
190: if ((lastActualSeen == null)
191: || (endTime > lastActualSeen.longValue())) {
192: endTimeMap.put(org, new Long(endTime));
193: }
194: }
195: }
196: }
197:
198: private void mapLevel2ToLevel6(Collection level2s,
199: Collection level6s) {
200: //alreadyMapped is only here to print out the error below. This turns out
201: //to be a nominal case where level 6s overlap more than one level2. Commented
202: //out the debug code for right now. Can uncomment if any need to re-examine.
203: //HashSet alreadyMapped = new HashSet();
204: level2To6Map.clear();
205: Iterator level2It = level2s.iterator();
206: while (level2It.hasNext()) {
207: Task level2Task = (Task) level2It.next();
208: long l2StartTime = getTaskUtils().getStartTime(level2Task);
209: long l2EndTime = getTaskUtils().getEndTime(level2Task);
210: Object l2Cust = getTaskUtils().getCustomer(level2Task);
211: Iterator level6It = level6s.iterator();
212: ArrayList mappedL6s = new ArrayList();
213: while (level6It.hasNext()) {
214: Task level6Task = (Task) level6It.next();
215: long l6StartTime = getTaskUtils().getStartTime(
216: level6Task);
217: long l6EndTime = getTaskUtils().getEndTime(level6Task);
218: Object l6Cust = getTaskUtils().getCustomer(level2Task);
219: if ((l6StartTime < l2EndTime)
220: && (l6EndTime > l2StartTime)) {
221: if (l2Cust.equals(l6Cust)) {
222: mappedL6s.add(level6Task);
223: /**
224: if ((alreadyMapped.contains(level6Task)) &&
225: logger.isWarnEnabled()) {
226: //Apparently lots overlap commented out alreadyMapped and all debug related code.
227: logger.warn("The following task has already been mapped: " + level6Task.getUID() + " startTime: " +
228: new Date(l6StartTime) + " endTime: " +
229: new Date(l6EndTime) + ". And the new overlapping L2 Task startTime " + new Date(l2StartTime) +
230: " and endTime is:" + new Date(l2EndTime));
231: } else {
232: alreadyMapped.add(level6Task);
233: }
234: **/
235: } else {
236: logger
237: .error("Unexpected Customer of level2Task "
238: + l2Cust
239: + " differs from level6 cust:"
240: + l6Cust);
241: }
242: }
243: }
244: level2To6Map.put(level2Task, mappedL6s);
245: }
246: }
247:
248: protected double deriveTotalQty(long bucketStart, long bucketEnd,
249: Collection projTasks) {
250: Iterator tasksIt = projTasks.iterator();
251: double totalQty = 0.0;
252: while (tasksIt.hasNext()) {
253: Task projTask = (Task) tasksIt.next();
254: double qty = deriveTotalQty(bucketStart, bucketEnd,
255: projTask);
256: totalQty += qty;
257: }
258: return totalQty;
259: }
260:
261: protected double deriveTotalQty(long bucketStart, long bucketEnd,
262: Task projTask) {
263:
264: long taskStart = getTaskUtils().getStartTime(projTask);
265: long taskEnd = getTaskUtils().getEndTime(projTask);
266: long start = Math.max(taskStart, bucketStart);
267: long end = Math.min(taskEnd, bucketEnd);
268: double qty = 0.0;
269: //duration in seconds
270: if (start < end) {
271: double duration = ((end - start) / 1000);
272: Rate rate = getTaskUtils().getRate(projTask);
273: qty = (getBaseUnitPerSecond(rate) * duration);
274: }
275: return qty;
276: }
277:
278: protected static double getBaseUnitPerSecond(Rate rate) {
279: if (rate instanceof CostRate) {
280: return ((CostRate) rate).getDollarsPerSecond();
281: } else if (rate instanceof CountRate) {
282: return ((CountRate) rate).getEachesPerSecond();
283: } else if (rate instanceof FlowRate) {
284: return ((FlowRate) rate).getGallonsPerSecond();
285: } else if (rate instanceof MassTransferRate) {
286: return ((MassTransferRate) rate).getShortTonsPerSecond();
287: } else if (rate instanceof TimeRate) {
288: return ((TimeRate) rate).getHoursPerSecond();
289: } // if
290: return 0.0;
291: }
292:
293: protected Rate newRateFromUnitPerSecond(Rate rate,
294: double unitsPerSecond) {
295: if (rate instanceof CostRate) {
296: return (CostRate.newDollarsPerSecond(unitsPerSecond));
297: } else if (rate instanceof CountRate) {
298: return (CountRate.newEachesPerSecond(unitsPerSecond));
299: } else if (rate instanceof FlowRate) {
300: return (FlowRate.newGallonsPerSecond(unitsPerSecond));
301: } else if (rate instanceof MassTransferRate) {
302: return (MassTransferRate
303: .newShortTonsPerSecond(unitsPerSecond));
304: } else if (rate instanceof TimeRate) {
305: return (TimeRate.newHoursPerSecond(unitsPerSecond));
306: } // if
307:
308: if (logger.isErrorEnabled()) {
309: logger.error("Unknown rate type");
310: }
311: return (CountRate.newEachesPerSecond(unitsPerSecond));
312: }
313:
314: private void expandLevel2Task(Task parent, Rate newRate,
315: long firstLegalStartTime) {
316:
317: NewTask childTask = createNewLevel2Task(parent, newRate,
318: firstLegalStartTime);
319: PlanElement pe = parent.getPlanElement();
320: if (pe == null) {
321: createAndPublishExpansion(parent, childTask);
322: } else if (pe instanceof Expansion) {
323: republishExpansionWithTask(parent, childTask);
324: } else if (pe instanceof Disposition) {
325: if (logger.isDebugEnabled()) {
326: logger
327: .debug("Level2Expander:expandLevel2Task: Expanding an already disposed task");
328: }
329: removeDisposition(parent);
330: createAndPublishExpansion(parent, childTask);
331: } else if (logger.isErrorEnabled()) {
332: logger.error("Unknown Plan Element on task-" + parent);
333: }
334:
335: }
336:
337: private void removeDisposition(Task level2Task) {
338: Disposition disposition = (Disposition) level2Task
339: .getPlanElement();
340: translatorPlugin.publishRemove(disposition);
341: }
342:
343: private void republishExpansionWithTask(Task parent,
344: NewTask childTask) {
345: Expansion expansion = (Expansion) parent.getPlanElement();
346: NewWorkflow wf = (NewWorkflow) expansion.getWorkflow();
347: removeAllFromWorkflow(wf);
348: if (logger.isDebugEnabled()) {
349: logger
350: .debug("translatorPlug in:Level2Expander:I'm re-publishing 1 Level2 "
351: + translatorPlugin.getSupplyType()
352: + " task.");
353: }
354: childTask.setWorkflow(wf);
355: translatorPlugin.publishAdd(childTask);
356: wf.addTask(childTask);
357: translatorPlugin.publishChange(expansion);
358: }
359:
360: private void removeAllFromWorkflow(NewWorkflow wf) {
361: Enumeration subtasks = wf.getTasks();
362: while (subtasks.hasMoreElements()) {
363: Task childTask = (Task) subtasks.nextElement();
364: wf.removeTask(childTask);
365: translatorPlugin.publishRemove(childTask);
366: }
367: }
368:
369: /**
370: * Define an ARA that can deal with the expansion of a
371: * Level2 ProjectSupply task to a Ready For Transport Level 2 ProjectSupply Task.
372: * Mostly, we just clone the result of the single chile Ready for Transport ProjectSupply
373: * task.
374: **/
375: private static class ExpansionARA implements
376: AllocationResultAggregator {
377: public AllocationResult calculate(Workflow wf,
378: TaskScoreTable tst, AllocationResult currentar) {
379: if (tst.size() != 1)
380: throw new IllegalArgumentException(
381: "expansionARA: multiple subtasks");
382: AllocationResult ar = (AllocationResult) tst
383: .getAllocationResult(0);
384: if (ar == null)
385: return null;
386: if (ar.isEqual(currentar))
387: return currentar;
388: return (AllocationResult) ar.clone();
389: }
390: }
391:
392: private void createAndPublishExpansion(Task parent,
393: NewTask childTask) {
394: Workflow wf = buildWorkflow(parent, childTask);
395: translatorPlugin.publishAdd(childTask);
396: Expansion expansion = getPlanningFactory().createExpansion(
397: parent.getPlan(), parent, wf, null);
398: translatorPlugin.publishAdd(expansion);
399: }
400:
401: /**
402: * Build a workflow from a vector of tasks.
403: * @param parent parent task of workflow
404: * @param childTask new child task to be added to workflow tasks
405: **/
406: public Workflow buildWorkflow(Task parent, NewTask childTask) {
407: NewWorkflow wf = getPlanningFactory().newWorkflow();
408: wf.setParentTask(parent);
409: wf.setIsPropagatingToSubtasks(true);
410: //wf.setAllocationResultAggregator(expansionARA);
411: childTask.setWorkflow(wf);
412: wf.addTask(childTask);
413:
414: return wf;
415: }
416:
417: private NewTask createNewLevel2Task(Task parentTask, Rate newRate,
418: long firstLegalStartTime) {
419:
420: Vector newPrefs = new Vector();
421: Enumeration oldPrefs = parentTask.getPreferences();
422:
423: long startTime = getTaskUtils().getStartTime(parentTask);
424:
425: while (oldPrefs.hasMoreElements()) {
426: Preference pref = (Preference) oldPrefs.nextElement();
427: if (pref.getAspectType() == AlpineAspectType.DEMANDRATE) {
428: pref = getTaskUtils().createDemandRatePreference(
429: getPlanningFactory(), newRate);
430: }
431: if ((firstLegalStartTime > startTime)
432: && (pref.getAspectType() == AspectType.START_TIME)) {
433: pref = createNewTimePreference(firstLegalStartTime,
434: pref);
435: }
436: newPrefs.add(pref);
437: }
438:
439: Vector newPreps = new Vector();
440: Enumeration oldPreps = parentTask.getPrepositionalPhrases();
441: while (oldPreps.hasMoreElements()) {
442: newPreps.add(oldPreps.nextElement());
443: }
444: newPreps.add(newPrepositionalPhrase(
445: Constants.Preposition.READYFORTRANSPORT,
446: translatorPlugin.getOrgName()));
447:
448: NewTask newTask = getPlanningFactory().newTask();
449:
450: newTask.setParentTask(parentTask);
451: newTask.setPlan(parentTask.getPlan());
452:
453: newTask.setDirectObject(parentTask.getDirectObject());
454: newTask.setVerb(parentTask.getVerb());
455:
456: newTask.setPreferences(newPrefs.elements());
457: newTask.setPrepositionalPhrases(newPreps.elements());
458:
459: newTask.setContext(parentTask.getContext());
460:
461: //newTask.setCommitmentDate(parentTask.getCommitmentDate());
462:
463: return newTask;
464: }
465:
466: /** Create a Time Preference for the Refill Task
467: * Use a Piecewise Linear Scoring Function.
468: * For details see the IM SDD.
469: * @param bestDay The time you want this preference to represent
470: * @param origTimePref A simarly generated time preference that was taken from the parent (we want a duplicate with a different best time)
471: * @return Preference The new Time Preference
472: **/
473: private Preference createNewTimePreference(long bestDay,
474: Preference origTimePref) {
475: int aspectType = origTimePref.getAspectType();
476: ScoringFunction.PiecewiseLinearScoringFunction origTimeSF = (ScoringFunction.PiecewiseLinearScoringFunction) origTimePref
477: .getScoringFunction();
478: AspectScoreRange origRange = origTimeSF.getDefinedRange();
479: long early = (long) origRange.getRangeStartPoint().getValue();
480: long late = getTimeUtils().addNDays(bestDay, 1);
481: long end = (long) origRange.getRangeEndPoint().getValue();
482: double daysBetween = ((end - bestDay) / 86400000);
483: //Use .0033 as a slope for now
484: double late_score = .0033 * daysBetween;
485: // define alpha .25
486: double alpha = .25;
487:
488: Vector points = new Vector();
489: AspectScorePoint earliest = new AspectScorePoint(AspectValue
490: .newAspectValue(aspectType, early), alpha);
491: AspectScorePoint best = new AspectScorePoint(AspectValue
492: .newAspectValue(aspectType, bestDay), 0.0);
493: AspectScorePoint first_late = new AspectScorePoint(AspectValue
494: .newAspectValue(aspectType, late), alpha);
495: AspectScorePoint latest = new AspectScorePoint(AspectValue
496: .newAspectValue(aspectType, end), (alpha + late_score));
497:
498: points.addElement(earliest);
499: points.addElement(best);
500: points.addElement(first_late);
501: points.addElement(latest);
502: ScoringFunction timeSF = ScoringFunction
503: .createPiecewiseLinearScoringFunction(points.elements());
504: return getPlanningFactory().newPreference(aspectType, timeSF);
505:
506: // prefs.addElement(TaskUtils.createDemandRatePreference(planFactory, rate));
507: //return prefs;
508: }
509:
510: protected PrepositionalPhrase newPrepositionalPhrase(
511: String preposition, Object io) {
512: NewPrepositionalPhrase pp = getPlanningFactory()
513: .newPrepositionalPhrase();
514: pp.setPreposition(preposition);
515: pp.setIndirectObject(io);
516: return pp;
517: }
518:
519: }
|