0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2001-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.logistics.plugin.trans;
0027:
0028: import org.cougaar.core.service.LoggingService;
0029: import org.cougaar.core.util.UID;
0030: import org.cougaar.glm.ldm.asset.*;
0031: import org.cougaar.glm.ldm.asset.PropertyGroupFactory;
0032: import org.cougaar.glm.ldm.plan.AlpineAspectType;
0033: import org.cougaar.lib.callback.UTILExpandableTaskCallback;
0034: import org.cougaar.lib.callback.UTILFilterCallback;
0035: import org.cougaar.lib.callback.UTILFilterCallbackAdapter;
0036: import org.cougaar.lib.callback.UTILGenericListener;
0037: import org.cougaar.lib.util.UTILAllocate;
0038: import org.cougaar.logistics.ldm.Constants;
0039: import org.cougaar.logistics.plugin.inventory.TaskUtils;
0040: import org.cougaar.planning.ldm.asset.*;
0041: import org.cougaar.planning.ldm.measure.Area;
0042: import org.cougaar.planning.ldm.measure.Mass;
0043: import org.cougaar.planning.ldm.measure.Volume;
0044: import org.cougaar.planning.ldm.plan.*;
0045: import org.cougaar.util.UnaryPredicate;
0046:
0047: import java.util.*;
0048:
0049: /**
0050: * Takes PROJECT_SUPPLY tasks
0051: * and converts them into TRANSPORT tasks with a RESERVATION prep. <p>
0052: *
0053: */
0054: public class AmmoProjectionExpanderPlugin extends
0055: AmmoLowFidelityExpanderPlugin {
0056: public long CHUNK_DAYS = 30;
0057: protected boolean FIND_FOR_UNIT_PREP_ON_TASK = false;
0058: public static long MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
0059: public static double SECS_PER_DAY = 60.0d * 60.0d * 24.0d;
0060: public static final String AMMO_CATEGORY_CODE = "MBB";
0061: public static final String MILVAN_NSN = "NSN/8115001682275";
0062: public static final double PACKING_LIMIT = 13.9; /* short tons */
0063: public static final long TASK_TRANSMISSION_DELAY = 1000 * 60 * 60;
0064: private static Asset MILVAN_PROTOTYPE = null;
0065: public static String START = "Start";
0066: protected Map childToParent = new HashMap();
0067: protected Map uidToTask = new HashMap();
0068: protected Map typeToTasks = new HashMap();
0069: protected UTILFilterCallback myTaskCallback;
0070: protected List defaultUnit;
0071: public TaskUtils taskUtils;
0072:
0073: /**
0074: * rely upon load-time introspection to set these services -
0075: * don't worry about revokation.
0076: */
0077: public void setLoggingService(LoggingService bs) {
0078: super .setLoggingService(bs);
0079: taskUtils = new TaskUtils(logger);
0080: }
0081:
0082: public void localSetup() {
0083: super .localSetup();
0084:
0085: try {
0086: if (getMyParams().hasParam("CHUNK_DAYS"))
0087: CHUNK_DAYS = getMyParams().getLongParam("CHUNK_DAYS");
0088: if (getMyParams().hasParam("FIND_FOR_UNIT_PREP_ON_TASK"))
0089: FIND_FOR_UNIT_PREP_ON_TASK = getMyParams()
0090: .getBooleanParam("FIND_FOR_UNIT_PREP_ON_TASK");
0091: } catch (Exception e) {
0092: if (isWarnEnabled()) {
0093: warn("got unexpected exception " + e);
0094: }
0095: }
0096: }
0097:
0098: /**
0099: * Uses a special callback that maintains two maps :
0100: *
0101: * a uidToTask map so we can do parent task lookup efficiently AND
0102: * a typeToTasks map so we can look up a list of tasks by what ammo type they are
0103: *
0104: * @see #getMatchingTasksByType
0105: */
0106: public void setupFilters() {
0107: super .setupFilters();
0108:
0109: addFilter(myTaskCallback = new UTILFilterCallbackAdapter(this ,
0110: logger) {
0111: protected UnaryPredicate getPredicate() {
0112: return new UnaryPredicate() {
0113: public boolean execute(Object o) {
0114: return (o instanceof Task);
0115: }
0116: };
0117: }
0118:
0119: public void reactToChangedFilter() {
0120: Enumeration addedtasks = getSubscription()
0121: .getAddedList();
0122: while (addedtasks.hasMoreElements()) {
0123: Task task = (Task) addedtasks.nextElement();
0124:
0125: if (!(task instanceof Task))
0126: error("huh? " + task + " is not an MPTask??");
0127:
0128: if (isInfoEnabled()) {
0129: info("uidToTask - mapping task "
0130: + task.getUID());
0131: }
0132:
0133: uidToTask.put(task.getUID(), task);
0134:
0135: // keep a ammo type to tasks map so we can later compare
0136: // an actual task with a reserved or vice versa by just examining
0137: // tasks with the same ammo type
0138: // see #getMatchingTasksByType
0139:
0140: if (task.getVerb().equals(Constants.Verb.TRANSPORT)) {
0141: Collection types = getTypesInContainer(task);
0142:
0143: for (Iterator iter = types.iterator(); iter
0144: .hasNext();) {
0145: String type = (String) iter.next();
0146: List tasksForType = (List) typeToTasks
0147: .get(type);
0148: if (tasksForType == null) {
0149: typeToTasks.put(type,
0150: tasksForType = new ArrayList());
0151: if (isInfoEnabled()) {
0152: info("adding type " + type
0153: + " to map.");
0154: }
0155: }
0156: tasksForType.add(task);
0157: }
0158: }
0159: }
0160:
0161: if (getSubscription().getRemovedList()
0162: .hasMoreElements()) {
0163: Set uniqueTasks = new HashSet();
0164: Enumeration removedtasks = getSubscription()
0165: .getRemovedList();
0166: while (removedtasks.hasMoreElements()) {
0167: Task task = (Task) removedtasks.nextElement();
0168:
0169: if (uniqueTasks.contains(task)) { // skip duplicate entries in removed list, if any
0170: if (isWarnEnabled()) {
0171: warn("duplicate entries in removed list, skipping "
0172: + task.getUID());
0173: }
0174: continue;
0175: } else {
0176: uniqueTasks.add(task);
0177: }
0178:
0179: if (uidToTask.remove(task.getUID()) == null) {
0180: if (isWarnEnabled()) {
0181: warn("no task in uidToTask map with uid "
0182: + task.getUID());
0183: }
0184: } else {
0185: if (isInfoEnabled()) {
0186: info("uidToTask - removing task uid "
0187: + task.getUID() + " from map.");
0188: }
0189: }
0190:
0191: // keep a ammo type to tasks map so we can later compare
0192: // an actual task with a reserved or vice versa by just examining
0193: // tasks with the same ammo type
0194: // see #getMatchingTasksByType
0195: if (task.getVerb().equals(
0196: Constants.Verb.TRANSPORT)) {
0197: Collection types = getTypesInContainer(task);
0198:
0199: for (Iterator iter = types.iterator(); iter
0200: .hasNext();) {
0201: String type = (String) iter.next();
0202: List tasksForType = (List) typeToTasks
0203: .get(type);
0204: if (tasksForType != null) { // might have been removed already
0205: if (!tasksForType.remove(task)) {
0206: if (isWarnEnabled()) {
0207: warn("no task "
0208: + task.getUID()
0209: + " in typeToTasks.");
0210: }
0211: }
0212: }
0213: }
0214: }
0215: }
0216: }
0217: }
0218: });
0219: }
0220:
0221: protected UTILFilterCallback createThreadCallback(
0222: UTILGenericListener bufferingThread) {
0223: if (isInfoEnabled())
0224: info(getName() + " : Filtering for Expandable Tasks...");
0225:
0226: myInputTaskCallback = new UTILExpandableTaskCallback(
0227: bufferingThread, logger) {
0228: protected UnaryPredicate getPredicate() {
0229: return new UnaryPredicate() {
0230: protected TaskUtils myTaskUtils = new TaskUtils(
0231: logger);
0232:
0233: public boolean execute(Object o) {
0234: if (o instanceof Task) {
0235: Task task = (Task) o;
0236: boolean hasTransport = task.getVerb()
0237: .equals(Constants.Verb.TRANSPORT);
0238: boolean isReserved = (task
0239: .getPrepositionalPhrase(START) != null);
0240: if (hasTransport && isReserved)
0241: return true;
0242:
0243: if (// (task.getPlanElement() == null ) && ** Now dealing with changed tasks **
0244: (task.getVerb()
0245: .equals(Constants.Verb.PROJECTSUPPLY))
0246: && (myTaskUtils.isLevel2(task))) {
0247: boolean isReadyForTransport = (myTaskUtils
0248: .isReadyForTransport(task));
0249: //TODO: MWD delete this debug line
0250: if ((isReadyForTransport)
0251: && logger.isDebugEnabled()) {
0252: logger
0253: .debug("AmmoProjectionExpanderPlugin:isLevel2 ReadyForTransport="
0254: + isReadyForTransport);
0255: }
0256: return isReadyForTransport;
0257: }
0258:
0259: return (// (task.getWorkflow() == null ) && ** Now dealing with changed tasks **
0260: // (task.getPlanElement() == null ) &&
0261: ((UTILGenericListener) myListener)
0262: .interestingTask(task));
0263: }
0264: return false;
0265: }
0266: };
0267: }
0268: };
0269:
0270: return myInputTaskCallback;
0271: }
0272:
0273: /**
0274: * State that we are interested in all transport tasks
0275: * @param task the task to test.
0276: * @return true if the tasks verb is SUPPLY, false otherwise
0277: */
0278: public boolean interestingTask(Task task) {
0279: boolean hasSupply = task.getVerb().equals(
0280: Constants.Verb.PROJECTSUPPLY);
0281: boolean hasTransport = task.getVerb().equals(
0282: Constants.Verb.TRANSPORT);
0283:
0284: if (isDebugEnabled() && hasSupply)
0285: debug(".interestingTask - processing PROJECT_SUPPLY task "
0286: + task.getUID());
0287: if (isDebugEnabled() && hasTransport)
0288: debug(".interestingTask - processing TRANSPORT task "
0289: + task.getUID());
0290:
0291: return (hasSupply || hasTransport);
0292: }
0293:
0294: int total = 0;
0295:
0296: /**
0297: * Implemented for UTILExpanderPlugin interface
0298: *
0299: * Break up project supply tasks into reserved transport tasks for
0300: * discrete periods of time. Takes the (demand rate X period) of the project
0301: * supply task and breaks this up total ammo up across several subtasks, one
0302: * per month.
0303: *
0304: * Called from handleTask, which is called from HandleProjectSupplyTask
0305: * @see #handleProjectSupplyTask
0306: * @return transport subtasks for the parent project supply
0307: */
0308: public Vector getSubtasks(Task parentTask) {
0309: if (!(parentTask.getVerb().equals(Constants.Verb.PROJECTSUPPLY)))
0310: error("Expecting a project supply task, got " + parentTask);
0311:
0312: Vector childTasks = new Vector();
0313:
0314: // create the d.o.
0315:
0316: Asset supplyAsset = parentTask.getDirectObject();
0317:
0318: Preference pref = prefHelper.getPrefWithAspectType(parentTask,
0319: AlpineAspectType.DEMANDRATE);
0320: double ratePerSec = prefHelper.getPreferenceBestValue(pref); // base number is in tons per second!
0321: double ratePerDay = ratePerSec * SECS_PER_DAY;
0322:
0323: Date readyAt = prefHelper.getReadyAt(parentTask);
0324: Date early = getEarlyDate(parentTask);
0325: Date best = prefHelper.getBestDate(parentTask);
0326: Date late = getLateDate(parentTask);
0327:
0328: if (early.getTime() < readyAt.getTime())
0329: early = readyAt;
0330: if (best.getTime() < readyAt.getTime())
0331: best = readyAt;
0332:
0333: long window = best.getTime() - readyAt.getTime();
0334: long originalWindowInDays = window / MILLIS_PER_DAY;
0335: long numSubtasks = window / (CHUNK_DAYS * MILLIS_PER_DAY);
0336: if (window - (numSubtasks * CHUNK_DAYS * MILLIS_PER_DAY) != 0)
0337: numSubtasks++;
0338:
0339: if (numSubtasks < 1) {
0340: error(getName() + ".getSubtasks - task "
0341: + parentTask.getUID()
0342: + " will create no subtasks? Window was "
0343: + originalWindowInDays + " days");
0344: }
0345:
0346: if (isInfoEnabled()) {
0347: info(getName() + ".getSubtasks - task "
0348: + parentTask.getUID() + " from " + readyAt + " to "
0349: + best + " will produce " + numSubtasks
0350: + " subtasks.");
0351: }
0352:
0353: String unit;
0354: if (FIND_FOR_UNIT_PREP_ON_TASK) {
0355: unit = (prepHelper.hasPrepNamed(parentTask,
0356: Constants.Preposition.FOR)) ? (String) prepHelper
0357: .getIndirectObject(parentTask,
0358: Constants.Preposition.FOR) : "null";
0359: } else {
0360: unit = getClusterName();
0361: }
0362:
0363: // create one subtask for every chunk set of days, with an asset that is the total
0364: // delivered over the period = days*ratePerDay
0365: long daysSoFar = 0;
0366: double totalQuantity = 0;
0367: double targetQuantity = ((double) (window / 1000l))
0368: * ratePerSec;
0369: Date lastBestDate = readyAt;
0370:
0371: if (isInfoEnabled()) {
0372: info(getName() + ".getSubtasks - task "
0373: + parentTask.getUID() + " target quantity "
0374: + targetQuantity + " windowInSec " + window / 1000l
0375: + " rate/sec " + ratePerSec);
0376: }
0377:
0378: for (int i = 0; i < (int) numSubtasks; i++) {
0379: boolean onLastTask = (window / MILLIS_PER_DAY) < CHUNK_DAYS;
0380: long daysToChunk = (onLastTask) ? window / MILLIS_PER_DAY
0381: : CHUNK_DAYS;
0382:
0383: if (isInfoEnabled() && onLastTask)
0384: info("on last task - days " + daysToChunk + " since "
0385: + window / MILLIS_PER_DAY + " < " + CHUNK_DAYS);
0386:
0387: daysSoFar += daysToChunk;
0388: window -= daysToChunk * MILLIS_PER_DAY;
0389: double quantity = ((double) daysToChunk) * ratePerDay;
0390: if (onLastTask
0391: && ((totalQuantity + quantity) != targetQuantity)) {
0392: if (isInfoEnabled())
0393: info(" task " + parentTask.getUID()
0394: + " adjusting quantity from " + quantity
0395: + " to " + (targetQuantity - totalQuantity)
0396: + " total is " + totalQuantity);
0397: quantity = targetQuantity - totalQuantity;
0398: } else if (isInfoEnabled()) {
0399: info(".getSubtasks - task " + parentTask.getUID()
0400: + " quantity is " + quantity + " chunk days "
0401: + daysToChunk + " rate " + ratePerDay);
0402: }
0403:
0404: if (quantity < 0.00001) {
0405: if (isInfoEnabled()) {
0406: info(".getSubtasks - task "
0407: + parentTask.getUID()
0408: + " gets a quantity of zero, ratePerDay was "
0409: + ratePerDay + " chunk days " + daysToChunk);
0410: }
0411: }
0412:
0413: // double massInKGs = ((GLMAsset)supplyAsset).getPhysicalPG().getMass().getKilograms()*quantity;
0414: double massInSTons = quantity;
0415: totalQuantity += quantity;
0416:
0417: // CHANGE to JUST Grab the d.o.?
0418:
0419: // set item id pg to show it's a reservation, and not a normal task's asset
0420:
0421: ItemIdentificationPG itemIDPG = (ItemIdentificationPG) supplyAsset
0422: .getItemIdentificationPG();
0423: String itemID = itemIDPG.getItemIdentification();
0424: String itemNomen = itemIDPG.getNomenclature();
0425:
0426: if (itemID == null) {
0427: TypeIdentificationPG typeID = supplyAsset
0428: .getTypeIdentificationPG();
0429: itemID = typeID.getTypeIdentification();
0430: }
0431:
0432: if (itemNomen == null) {
0433: TypeIdentificationPG typeID = supplyAsset
0434: .getTypeIdentificationPG();
0435: itemNomen = typeID.getNomenclature();
0436: }
0437:
0438: Asset directObject = getMilvanDirectObject(itemNomen,
0439: itemID, unit, massInSTons);
0440:
0441: long bestTime = getBestTime(early, best, daysSoFar);
0442:
0443: Task subTask = createSubtask(parentTask, directObject,
0444: early, best, late, bestTime, daysSoFar,
0445: lastBestDate, itemNomen, massInSTons);
0446:
0447: lastBestDate = new Date(bestTime);
0448: childTasks.addElement(subTask);
0449: }
0450:
0451: // post condition
0452: if (totalQuantity > targetQuantity + 0.001
0453: || totalQuantity < targetQuantity - 0.001) {
0454: if (isWarnEnabled()) {
0455: warn(getName() + " total quantity " + totalQuantity
0456: + " != original total " + targetQuantity
0457: + " = window " + originalWindowInDays
0458: + " * ratePerDay " + ratePerDay);
0459: }
0460: }
0461:
0462: if (isInfoEnabled())
0463: info(getName() + " returning " + childTasks.size()
0464: + " subtasks for " + parentTask.getUID());
0465:
0466: return childTasks;
0467: }
0468:
0469: /**
0470: * make sure best time is before or equal to best time on task
0471: */
0472: protected long getBestTime(Date early, Date best, long daysSoFar) {
0473: long bestTime = early.getTime() + ((long) daysSoFar)
0474: * MILLIS_PER_DAY;
0475: if (bestTime > best.getTime()) {
0476: if (isInfoEnabled()) {
0477: info(getName()
0478: + ".getSubtasks - had to correct bestTime, was "
0479: + new Date(bestTime) + " now " + best);
0480: }
0481:
0482: bestTime = best.getTime();
0483: }
0484:
0485: return bestTime;
0486: }
0487:
0488: /**
0489: * Create the transport reservation subtask of the parent.
0490: */
0491: protected Task createSubtask(Task parentTask, Asset directObject,
0492: Date early, Date best, Date late, long bestTime,
0493: long daysSoFar, Date lastBestDate, String itemNomen,
0494: double massInSTons) {
0495: Task subTask = makeTask(parentTask, directObject);//deliveredAsset);
0496: Date startDate = new Date(getAlarmService().currentTimeMillis()
0497: + TASK_TRANSMISSION_DELAY);
0498: if (isInfoEnabled())
0499: info(getName() + ".getSubtasks - making task "
0500: + subTask.getUID() + " with best arrival "
0501: + new Date(bestTime) + " and start " + startDate);
0502:
0503: // prevent earliest arrival being before start date
0504: if (early.getTime() < startDate.getTime()) {
0505: early = startDate;
0506: }
0507:
0508: prefHelper.replacePreference((NewTask) subTask, prefHelper
0509: .makeEndDatePreference(ldmf, early, new Date(bestTime),
0510: late));
0511:
0512: prefHelper.replacePreference((NewTask) subTask, prefHelper
0513: .makeStartDatePreference(ldmf, startDate));
0514:
0515: prefHelper.removePrefWithAspectType(subTask,
0516: AlpineAspectType.DEMANDRATE); // we've included it in the d.o.
0517:
0518: prepHelper.removePrepNamed(subTask,
0519: Constants.Preposition.MAINTAINING);
0520: prepHelper.removePrepNamed(subTask,
0521: Constants.Preposition.REFILL);
0522: prepHelper.addPrepToTask(subTask, prepHelper
0523: .makePrepositionalPhrase(ldmf, "Start", lastBestDate));
0524:
0525: if (!FIND_FOR_UNIT_PREP_ON_TASK) {
0526: prepHelper.replacePrepOnTask(subTask,
0527: prepHelper
0528: .makePrepositionalPhrase(ldmf,
0529: Constants.Preposition.FOR,
0530: getClusterName()));
0531: }
0532:
0533: if (isInfoEnabled()) {
0534: info(getName() + " publishing reservation "
0535: + subTask.getUID() + " for " + itemNomen + " from "
0536: + lastBestDate + " to " + new Date(bestTime)
0537: + " weight " + massInSTons + " short tons.");
0538: }
0539:
0540: return subTask;
0541: }
0542:
0543: private static final double MAX_IN_MILVAN = 13.9;
0544:
0545: protected Asset getMilvanDirectObject(String itemNomen,
0546: String itemID, String unit, double massInSTons) {
0547: int numMilvans = (int) Math.ceil(massInSTons / MAX_IN_MILVAN);
0548: if (numMilvans == 0) {
0549: numMilvans++;
0550: if (isWarnEnabled())
0551: warn("Got mass that was zero : " + massInSTons);
0552: massInSTons += 0.1;
0553: }
0554: double tonsLeft = massInSTons;
0555:
0556: if (numMilvans == 1) {
0557: GLMAsset milvan = makeMilvan();
0558: addContentsInfo(milvan, itemNomen, itemID, unit,
0559: massInSTons);
0560: return milvan;
0561: } else {
0562: Vector milvans = new Vector();
0563: for (int i = 0; i < numMilvans; i++) {
0564: GLMAsset milvan = makeMilvan();
0565: double amount = Math.min(tonsLeft, MAX_IN_MILVAN);
0566: addContentsInfo(milvan, itemNomen, itemID, unit, amount);
0567: tonsLeft -= amount;
0568: milvans.add(milvan);
0569: }
0570: if ((tonsLeft > 0.0000001) || (tonsLeft < -0.0000001)) {
0571: error("Tons left is not zero = " + tonsLeft);
0572: }
0573: return assetHelper.makeAssetGroup(getLDMService().getLDM()
0574: .getFactory(), milvans);
0575: }
0576: }
0577:
0578: public Date getEarlyDate(Task t) {
0579: Preference endDatePref = prefHelper.getPrefWithAspectType(t,
0580: AspectType.END_TIME);
0581: AspectScoreRange range = endDatePref.getScoringFunction()
0582: .getDefinedRange();
0583: return new Date(((AspectScorePoint) range.getRangeStartPoint())
0584: .getAspectValue().longValue());
0585: }
0586:
0587: public Date getLateDate(Task t) {
0588: Preference endDatePref = prefHelper.getPrefWithAspectType(t,
0589: AspectType.END_TIME);
0590: AspectScoreRange range = endDatePref.getScoringFunction()
0591: .getDefinedRange();
0592: return new Date(((AspectScorePoint) range.getRangeEndPoint())
0593: .getAspectValue().longValue());
0594: }
0595:
0596: protected Enumeration getValidEndDateRanges(Preference endDatePref) {
0597: Calendar cal = java.util.Calendar.getInstance();
0598: cal.set(2200, 0, 0, 0, 0, 0);
0599: cal.set(Calendar.MILLISECOND, 0);
0600: Date endOfRange = (Date) cal.getTime();
0601:
0602: Enumeration validRanges = endDatePref
0603: .getScoringFunction()
0604: .getValidRanges(
0605: TimeAspectValue.create(AspectType.END_TIME, 0l),
0606: TimeAspectValue.create(AspectType.END_TIME,
0607: endOfRange));
0608: return validRanges;
0609: }
0610:
0611: /** create aggregate asset aggregating the direct object's prototype **/
0612: protected AggregateAsset createDeliveredAsset(Task originalTask,
0613: Asset originalAsset, int quantity) {
0614: Asset prototype = originalAsset.getPrototype();
0615:
0616: if (prototype == null) {
0617: prototype = originalAsset;
0618: GLMAsset glmProto = (GLMAsset) prototype;
0619: if (!glmProto.hasPhysicalPG()) {
0620: warn("createDeliveredAsset - task "
0621: + originalTask.getUID() + "'s d.o. "
0622: + prototype.getUID() + " - " + prototype
0623: + " doesn't have a physical PG - "
0624: + glmProto.getPhysicalPG());
0625: if (!(prototype instanceof PhysicalAsset))
0626: error("createDeliveredAsset - task "
0627: + originalTask.getUID() + "'s d.o. "
0628: + prototype + " is not a physical asset?.");
0629: else if (isInfoEnabled()) {
0630: info("createDeliveredAsset - task "
0631: + originalTask.getUID() + "'s d.o. "
0632: + prototype.getUID()
0633: + " is a physical asset.");
0634: }
0635: } else {
0636: if (glmProto.getPhysicalPG().getFootprintArea() == null) {
0637: ((NewPhysicalPG) glmProto.getPhysicalPG())
0638: .setFootprintArea(new Area(
0639: Area.SQUARE_FEET, 1));
0640: if (isWarnEnabled()) {
0641: warn("createDeliveredAsset - task "
0642: + originalTask.getUID()
0643: + "'s d.o. "
0644: + prototype.getUID()
0645: + " doesn't have an area slot on its physical pg.");
0646: }
0647: }
0648: if (glmProto.getPhysicalPG().getVolume() == null) {
0649: ((NewPhysicalPG) glmProto.getPhysicalPG())
0650: .setVolume(new Volume(Volume.CUBIC_FEET, 1));
0651: if (isWarnEnabled()) {
0652: warn("createDeliveredAsset - task "
0653: + originalTask.getUID()
0654: + "'s d.o. "
0655: + prototype.getUID()
0656: + " doesn't have a volume slot on its physical pg.");
0657: }
0658: }
0659: }
0660: if (!glmProto.hasPackagePG()) {
0661: warn("createDeliveredAsset - task "
0662: + originalTask.getUID() + "'s d.o. "
0663: + prototype.getUID() + " - " + prototype
0664: + " doesn't have a package PG.");
0665: }
0666: }
0667:
0668: AggregateAsset deliveredAsset = (AggregateAsset) ldmf
0669: .createAggregate(prototype, quantity);
0670:
0671: return deliveredAsset;
0672: }
0673:
0674: /**
0675: * <pre>
0676: * Report to superior that the expansion has changed.
0677: *
0678: * An override is needed here for two reasons :
0679: *
0680: * 1) to add the DEMANDRATE preference value into the allocation result,
0681: * since downstream plugins won't set this aspect.
0682: * 2) echo the start time preference in the allocation result (why?)
0683: *
0684: * No allocation results flow upward unless reported confidence reaches 100%.
0685: *
0686: * </pre>
0687: * @param cpe Expansion that has changed.
0688: * @see org.cougaar.lib.filter.UTILPluginAdapter#updateAllocationResult
0689: */
0690: public void reportChangedExpansion(Expansion cpe) {
0691: if (isDebugEnabled())
0692: debug(getName() + " : Received changed pe " + cpe.getUID()
0693: + " for task " + cpe.getTask().getUID());
0694: AllocationResult reportedresult = cpe.getReportedResult();
0695: if (reportedresult != null) {
0696: // compare entire allocationresults.
0697: AllocationResult estimatedresult = cpe.getEstimatedResult();
0698: double confidence = reportedresult.getConfidenceRating();
0699: boolean nullEstimated = (estimatedresult == null);
0700: // if we are not ignoring low confidence reported values
0701: boolean highConfidence = (!skipLowConfidence || confidence > HIGH_CONFIDENCE);
0702:
0703: if (nullEstimated
0704: || (highConfidence && (!isEqual(estimatedresult,
0705: reportedresult)))) {
0706: if (isDebugEnabled())
0707: debug(getName()
0708: + " : Swapping Alloc Results for task "
0709: + cpe.getTask().getUID());
0710: if (isWarnEnabled() && !reportedresult.isSuccess())
0711: warn(getName() + " : " + cpe.getTask().getUID()
0712: + " failed to allocate.");
0713:
0714: cpe.setEstimatedResult(reportedresult);
0715:
0716: // double prefValue = getScaledRate (cpe);
0717: Task task = cpe.getTask();
0718: AspectValue prefRate = task.getPreference(
0719: AlpineAspectType.DEMANDRATE)
0720: .getScoringFunction().getBest()
0721: .getAspectValue();
0722:
0723: AspectValue[] aspectValues = cpe.getEstimatedResult()
0724: .getAspectValueResults();
0725:
0726: AspectValue[] copy = new AspectValue[aspectValues.length + 1];
0727: System.arraycopy(aspectValues, 0, copy, 0,
0728: aspectValues.length);
0729: copy[aspectValues.length] = prefRate;
0730:
0731: // fix start time to echo start time preference
0732: for (int i = 0; i < copy.length; i++) {
0733: AspectValue value = copy[i];
0734: if (value.getAspectType() == AspectType.START_TIME) {
0735: Date preferredStart = prefHelper
0736: .getReadyAt(task);
0737: copy[i] = AspectValue.newAspectValue(
0738: AspectType.START_TIME, preferredStart);
0739: break;
0740: }
0741: }
0742:
0743: AllocationResult correctedAR = new AllocationResult(
0744: reportedresult.getConfidenceRating(),
0745: reportedresult.isSuccess(), copy);
0746:
0747: cpe.setEstimatedResult(correctedAR);
0748:
0749: if (isInfoEnabled())
0750: info(getName() + " : publish changing task "
0751: + cpe.getTask().getUID());
0752:
0753: blackboard.publishChange(cpe);
0754: }
0755: } else if (!cpe.getTask().getSource().equals(
0756: getAgentIdentifier())) {
0757: error("ERROR! " + getName() + " : "
0758: + cpe.getTask().getUID()
0759: + " has a null reported allocation.");
0760: }
0761: }
0762:
0763: /**
0764: * <pre>
0765: * scale the rate to make it the rate over the time of the performance of the
0766: * task, so that when the inventory does days*rate, they come up with the original
0767: * requested quantity.
0768: *
0769: * The problem is that we're translating from one meaning of start and end date to another.
0770: * The start->end date window on a project supply task means "I need X widgets per day, each
0771: * day, over this period." The transportation start->end window means "The move started on this
0772: * day and ended on this other day." So they mean different things.
0773: * </pre>
0774: */
0775: protected double getScaledRate(PlanElement planElement) {
0776: Task task = planElement.getTask();
0777: double prefRate = task.getPreference(
0778: AlpineAspectType.DEMANDRATE).getScoringFunction()
0779: .getBest().getAspectValue().getValue();
0780:
0781: Date preferredStart = prefHelper.getReadyAt(task);
0782: Date preferredEnd = prefHelper.getBestDate(task);
0783:
0784: Date reportedStart = prefHelper.getReportedReadyAt(planElement);
0785: Date reportedEnd = prefHelper.getReportedEndDate(planElement);
0786:
0787: long preferredWindow = preferredEnd.getTime()
0788: - preferredStart.getTime();
0789: long reportedWindow = reportedEnd.getTime()
0790: - reportedStart.getTime();
0791:
0792: double ratio = (double) preferredWindow
0793: / (double) reportedWindow;
0794:
0795: return prefRate * ratio;
0796: }
0797:
0798: /**
0799: * checks to see if the AllocationResult is equal to this one.
0800: *
0801: * We have to use this copy of the AllocationResult.isEqual because we want to
0802: * to skip equals comparison of DEMANDRATE and START_TIME aspects. (why?)
0803: *
0804: * Mainly we need our own version of AspectValue.nearlyEquals
0805: *
0806: * @param thisAR
0807: * @param that
0808: * @return boolean
0809: */
0810: public boolean isEqual(AllocationResult this AR,
0811: AllocationResult that) {
0812: if (this AR == that)
0813: return true; // quick success
0814: if (that == null)
0815: return false; // quick fail
0816: if (!(this AR.isSuccess() == that.isSuccess()
0817: && this AR.isPhased() == that.isPhased() && this AR
0818: .getConfidenceRating() == that.getConfidenceRating())) {
0819: if (isInfoEnabled())
0820: info("AspectValues - success/phased/confidence this AR "
0821: + this AR + " != " + that);
0822: return false;
0823: }
0824:
0825: //check the real stuff now!
0826: //check the aspect types
0827: //check the summary results
0828: synchronized (this AR.getAspectValueResults()) {
0829: if (!nearlyEquals(this AR.getAspectValueResults(), that
0830: .getAspectValueResults())) {
0831: if (isDebugEnabled())
0832: debug("AspectValues - this AR " + this AR + " != "
0833: + that);
0834: return false;
0835: }
0836: // check the phased results
0837: if (this AR.isPhased()) {
0838: Iterator i1 = that.getPhasedAspectValueResults()
0839: .iterator();
0840: Iterator i2 = this AR.getPhasedAspectValueResults()
0841: .iterator();
0842: while (i1.hasNext()) {
0843: if (!i2.hasNext())
0844: return false;
0845: if (!nearlyEquals((AspectValue[]) i1.next(),
0846: (AspectValue[]) i2.next())) {
0847: if (isDebugEnabled())
0848: debug("phased AspectValues - this AR "
0849: + this AR + " != " + that);
0850: return false;
0851: }
0852: }
0853: if (i2.hasNext())
0854: return false;
0855: }
0856: }
0857:
0858: // check the aux queries
0859:
0860: // AUX QUERIES are not used in this plugin - honestly, where are they used?
0861:
0862: /*
0863: String[] taux = that.auxqueries;
0864: if (auxqueries != taux) {
0865: if (!Arrays.equals(taux, auxqueries)) return false;
0866: }
0867: */
0868:
0869: // must be equals...
0870: return true;
0871: }
0872:
0873: /**
0874: * checks to see that two aspect value arrays are equal
0875: *
0876: * skips comparison of DEMANDRATE and START_TIME aspects.
0877: *
0878: * We have to skip start time, since it doesn't mean the same in the inventory world
0879: * as in the transportation world.
0880: *
0881: * @param avs1 first set
0882: * @param avs2 second set
0883: * @return true if the same
0884: */
0885: public boolean nearlyEquals(AspectValue[] avs1, AspectValue[] avs2) {
0886: int len = avs1.length;
0887: // if (len != avs2.length) return false; // Can't be equal if different length
0888: outer: for (int i = 0; i < len; i++) {
0889: AspectValue av1 = avs1[i];
0890: int type1 = av1.getAspectType();
0891: if (type1 == AlpineAspectType.DEMANDRATE)
0892: continue; // ignore DEMAND RATE!
0893: if (type1 == AspectType.START_TIME)
0894: continue; // ignore Start time, since it doesn't mean the same in the inventory world
0895: inner: for (int j = 0; j < len; j++) {
0896: int k = (i + j) % len;
0897: AspectValue av2 = avs2[k];
0898: int type2 = av2.getAspectType();
0899: if (type1 == type2) {
0900: if (av1.nearlyEquals(av2))
0901: continue outer;
0902: break inner;
0903: }
0904: }
0905: return false; // Found no match
0906: }
0907: return true; // Found a match for every aspect
0908: }
0909:
0910: /**
0911: * An ancillary method that creates an asset that represents a MILVAN
0912: * (military container) carrying ammunition
0913: */
0914: protected GLMAsset makeMilvan() {
0915:
0916: if (MILVAN_PROTOTYPE == null) {
0917: MILVAN_PROTOTYPE = getLDMService().getLDM().getPrototype(
0918: MILVAN_NSN);
0919:
0920: if (MILVAN_PROTOTYPE == null) {
0921: error("AmmoTransport: Error! Unable to get prototype for"
0922: + " milvan NSN -" + MILVAN_NSN);
0923: return null;
0924: }
0925: }
0926:
0927: String itemID = makeMilvanID();
0928: Container milvan = (Container) getLDMService().getLDM()
0929: .getFactory().createInstance(MILVAN_PROTOTYPE, itemID);
0930:
0931: // AMMO Cargo Code
0932: NewMovabilityPG movabilityPG = PropertyGroupFactory
0933: .newMovabilityPG(milvan.getMovabilityPG());
0934: movabilityPG.setCargoCategoryCode(AMMO_CATEGORY_CODE);
0935: milvan.setMovabilityPG(movabilityPG);
0936:
0937: // Milvan Contents
0938: NewContentsPG contentsPG = PropertyGroupFactory.newContentsPG();
0939: milvan.setContentsPG(contentsPG);
0940:
0941: // Unique Item Identification
0942: NewItemIdentificationPG itemIdentificationPG = PropertyGroupFactory
0943: .newItemIdentificationPG();
0944:
0945: // String itemID = makeMilvanID();
0946: itemIdentificationPG.setItemIdentification(itemID); // redundant?
0947: itemIdentificationPG.setNomenclature("Milvan");
0948: itemIdentificationPG.setAlternateItemIdentification(itemID);
0949: milvan.setItemIdentificationPG(itemIdentificationPG);
0950:
0951: return milvan;
0952: }
0953:
0954: protected String makeMilvanID() {
0955: return new String("Reserved_" + getCounter());
0956: }
0957:
0958: private static int COUNTER = 0;
0959:
0960: private static synchronized long getCounter() {
0961: return COUNTER++;
0962: }
0963:
0964: protected void addContentsInfo(GLMAsset container, String nomen,
0965: String typeID, String unit, double massInSTons) {
0966: List typeIDs = new ArrayList();
0967: List nomenclatures = new ArrayList();
0968: List weights = new ArrayList();
0969: List receivers = new ArrayList();
0970:
0971: typeIDs.add(typeID);
0972: nomenclatures.add(nomen);
0973:
0974: Mass mass = Mass.newMass(massInSTons, Mass.SHORT_TONS);
0975: weights.add(mass);
0976:
0977: receivers.add(unit);
0978:
0979: // Contents
0980: NewContentsPG contentsPG = PropertyGroupFactory.newContentsPG();
0981: contentsPG.setNomenclatures(nomenclatures);
0982: contentsPG.setTypeIdentifications(typeIDs);
0983: contentsPG.setWeights(weights);
0984: contentsPG.setReceivers(receivers);
0985: container.setContentsPG(contentsPG);
0986: }
0987:
0988: /**
0989: * Implemented for UTILBufferingPlugin interface
0990: *
0991: * @param tasks that have been buffered up to this point
0992: * @see org.cougaar.lib.filter.UTILBufferingPlugin#processTasks
0993: */
0994: public void processTasks(List tasks) {
0995: if (isInfoEnabled()) {
0996: info(getName() + ".processTasks - processing "
0997: + tasks.size() + " tasks.");
0998: }
0999:
1000: Map reservedToActual = new HashMap();
1001: for (int i = 0; i < tasks.size(); i++) {
1002: Task task = (Task) tasks.get(i);
1003:
1004: if (task.getVerb().equals(Constants.Verb.TRANSPORT))
1005: handleTransportTask(task, reservedToActual);
1006: else
1007: handleProjectSupplyTask(task);
1008: }
1009:
1010: for (Iterator iter = reservedToActual.keySet().iterator(); iter
1011: .hasNext();) {
1012: Task reserved = (Task) iter.next();
1013: Task actual = (Task) reservedToActual.get(reserved);
1014: Task reservedParent = getParentTask(reserved);
1015: if (reservedParent == null) {
1016: if (isInfoEnabled()) {
1017: info("skipping reserved task " + reserved.getUID()
1018: + " with no parent on blackboard.");
1019: }
1020: } else if (reserved.getWorkflow() == null) {
1021: if (isInfoEnabled()) {
1022: info("skipping reserved task " + reserved.getUID()
1023: + " with no workflow.");
1024: }
1025: } else {
1026: synchronized (reserved.getWorkflow()) {
1027: if (ownWorkflow(reserved)) {
1028: dealWithReservedTask(actual, reserved,
1029: reservedParent);
1030: } else if (isInfoEnabled()) {
1031: info("reserved task " + reserved.getUID()
1032: + " not a member of it's own workflow "
1033: + reserved.getWorkflow()
1034: + "\nworkflow task uids : "
1035: + uidsWorkflow(reserved)
1036: + " - assuming it will be removed.");
1037: }
1038: }
1039: }
1040: }
1041: }
1042:
1043: /**
1044: * Handle a task with verb Supply
1045: */
1046: public void handleProjectSupplyTask(Task t) {
1047: wantConfidence = true;
1048: if (t.getPlanElement() != null) {
1049: publishRemove(t.getPlanElement());
1050: }
1051: handleTask(t); // expands t with subtasks from getSubtasks
1052: Preference pref = prefHelper.getPrefWithAspectType(t,
1053: AlpineAspectType.DEMANDRATE);
1054: AspectValue ratePerSec = pref.getScoringFunction().getBest()
1055: .getAspectValue();
1056: if (isInfoEnabled())
1057: info(getName() + ".handleTask - task " + t.getUID()
1058: + " had p.e. " + t.getPlanElement().getUID());
1059: if (t.getPlanElement() instanceof Expansion) {
1060: addToEstimatedAR(t.getPlanElement(), ratePerSec);
1061: if (isInfoEnabled()) {
1062: Workflow tasksWorkflow = t.getWorkflow();
1063: Workflow peWorkflow = ((Expansion) t.getPlanElement())
1064: .getWorkflow();
1065: info(getName()
1066: + ".handleTask "
1067: + t.getUID()
1068: + " in "
1069: + ((tasksWorkflow != null) ? tasksWorkflow
1070: .getUID().toString() : "null wf?")
1071: + " p.e. "
1072: + t.getPlanElement().getUID()
1073: + " p.e. wf. "
1074: + ((peWorkflow != null) ? peWorkflow.getUID()
1075: .toString() : " null p.e. wf?"));
1076: }
1077: } else if (isWarnEnabled())
1078: warn(getName() + ".handleTask - task " + t.getUID()
1079: + " had no p.e.???");
1080: }
1081:
1082: protected void addToEstimatedAR(PlanElement exp, AspectValue rate) {
1083: AllocationResult estAR = exp.getEstimatedResult();
1084: AspectValue[] aspectValues = estAR.getAspectValueResults();
1085: AspectValue[] copy = new AspectValue[aspectValues.length + 1];
1086: System.arraycopy(aspectValues, 0, copy, 0, aspectValues.length);
1087: copy[aspectValues.length] = rate;
1088:
1089: AllocationResult replacement = ldmf.newAllocationResult(
1090: UTILAllocate.MEDIUM_CONFIDENCE, true, copy);
1091: exp.setEstimatedResult(replacement);
1092: }
1093:
1094: /**
1095: * Queries the uid->task map for the parent task of the child...
1096: * must do this since child task has a uid reference to the parent task not
1097: * an actual reference.
1098: */
1099: protected Task getParentTask(final Task child) {
1100: Task parent = (Task) uidToTask.get(child.getParentTaskUID());
1101:
1102: if (parent == null) {
1103: logger.warn("huh? no task with " + child.getParentTaskUID()
1104: + " in uid->task map.");
1105: }
1106:
1107: return parent;
1108: }
1109:
1110: /**
1111: * <pre>
1112: *
1113: * find matching reservation transport task
1114: * see if date overlaps
1115: * if it does, publish remove it and replace it with one with altered date span and quantity
1116: *
1117: * OK - could be MUCH more efficient - blackboard queries are extremely slow!
1118: *
1119: * </pre>
1120: */
1121: public void handleTransportTask(Task task1, Map reservedToActual) {
1122: // find matching reservation transport task
1123: final Task task = task1;
1124: final Collection units = findForPreps(task);
1125: final boolean isReserved = isReservedTask(task);
1126:
1127: if (isReservedTask(task) && !ownWorkflow(task)) {
1128: if (isInfoEnabled())
1129: info(".handleTransportTask - skipping reserved task "
1130: + task.getUID()
1131: + " that's not in it's own workflow.");
1132: return;
1133: }
1134:
1135: if (isInfoEnabled())
1136: info(getName()
1137: + ".handleTransportTask - looking through blackboard for task to match "
1138: + ((isReserved) ? "reserved " : "normal ")
1139: + task.getUID());
1140:
1141: Collection matchingTaskCollection = getMatchingTasksByType(
1142: task, units, isReserved);
1143:
1144: // there can be more than one matching reserved task, e.g. when the actual contains multiple different DODICS:
1145: // types [DODIC/C787, DODIC/C380, ] vs [DODIC/C787] (Task 1)
1146: // types [DODIC/C787, DODIC/C380, ] vs [DODIC/C380] (Task 2)
1147:
1148: if (matchingTaskCollection.isEmpty() && isInfoEnabled()) {
1149: info(".handleTransportTask - could not find matching task for "
1150: + task.getUID());
1151: return;
1152: }
1153: if (isInfoEnabled()) {
1154: info(".handleTransportTask - found "
1155: + matchingTaskCollection.size()
1156: + " matches for task " + task.getUID());
1157: }
1158:
1159: for (Iterator iter = matchingTaskCollection.iterator(); iter
1160: .hasNext();) {
1161: Task reservedTask, actual;
1162:
1163: if (isReserved) {
1164: reservedTask = task;
1165: actual = (Task) iter.next();
1166: } else {
1167: reservedTask = (Task) iter.next();
1168: actual = task;
1169: }
1170:
1171: if (!ownWorkflow(reservedTask)) {
1172: if (isInfoEnabled()) {
1173: info(".handleTransportTask - huh? reserved task "
1174: + reservedTask.getUID()
1175: + " not a member of it's own workflow "
1176: + reservedTask.getWorkflow() + "\nuids "
1177: + uidsWorkflow(reservedTask));
1178: }
1179: } else {
1180: if (isInfoEnabled()) {
1181: info(".handleTransportTask - updating map with reserved task "
1182: + reservedTask.getUID()
1183: + " and actual "
1184: + actual.getUID());
1185: }
1186: }
1187:
1188: updateMap(reservedToActual, actual, reservedTask);
1189: }
1190: }
1191:
1192: /**
1193: * Find those tasks of the same ammo type and try to find tasks that overlap in
1194: * time with the given task.
1195: *
1196: * @param task to look for matching tasks for
1197: * @param units units the task is for
1198: * @param isReserved is the task a reserved (project supply child) task
1199: * @return List of tasks that are for the same type of ammo, same unit, and for an overlapping period of time
1200: */
1201: protected List getMatchingTasksByType(Task task, Collection units,
1202: boolean isReserved) {
1203: List matches = new ArrayList();
1204:
1205: Collection types = getTypesInContainer(task);
1206:
1207: // reduce set of possible matching tasks to those for the same types of ammo
1208: List possibleMatches = new ArrayList();
1209: for (Iterator iter = types.iterator(); iter.hasNext();) {
1210: String type = (String) iter.next();
1211:
1212: List tasksForType = (List) typeToTasks.get(type);
1213: if (tasksForType == null) {
1214: if (isWarnEnabled()) {
1215: warn("Could not find tasks for type <" + type
1216: + "> for task " + task.getUID()
1217: + " with d.o. " + task.getDirectObject()
1218: + " and types <" + types + ">");
1219: }
1220: } else {
1221: possibleMatches.addAll(tasksForType);
1222: }
1223: }
1224:
1225: if (isInfoEnabled()) {
1226: info("examining " + possibleMatches.size()
1227: + " possible matches for task " + task.getUID());
1228: }
1229:
1230: for (Iterator iter = possibleMatches.iterator(); iter.hasNext();) {
1231: Task examinedTask = (Task) iter.next();
1232: if (task.getUID().equals(examinedTask.getUID())) {
1233: if (isInfoEnabled()) {
1234: info("skipping self " + examinedTask.getUID());
1235: }
1236:
1237: continue; // don't match yourself
1238: }
1239:
1240: // better be a transport task
1241: /*
1242: if (!(examinedTask.getVerb ().equals (Constants.Verb.TRANSPORT))) {
1243: if (isDebugEnabled())
1244: debug ("skipping non-transport task " + examinedTask.getUID());
1245: continue;
1246: }
1247: */
1248:
1249: // is it a reservation task?
1250: boolean examinedIsReserved = isReservedTask(examinedTask);
1251: if ((!isReserved && !examinedIsReserved)
1252: || (isReserved && examinedIsReserved)) {
1253: if (isInfoEnabled()) {
1254: info("skipping examined transport task because same type "
1255: + examinedTask.getUID()
1256: + " and "
1257: + task.getUID());
1258: }
1259: continue;
1260: }
1261:
1262: if (examinedIsReserved) {
1263: // has it already been removed from workflow?
1264: if (!taskInWorkflow(examinedTask, examinedTask
1265: .getWorkflow())) {
1266: if (isInfoEnabled())
1267: info("skipping reserved transport task "
1268: + examinedTask.getUID()
1269: + " since it's already been removed from it's workflow.");
1270: continue;
1271: }
1272: } else if (!(task instanceof MPTask)) {
1273: if (isInfoEnabled())
1274: info("saw transport task " + task.getUID()
1275: + " vs examined " + examinedTask.getUID());
1276: }
1277:
1278: // is it for the same org?
1279: Collection examinedUnits = findForPreps(examinedTask);
1280: Collection copy = new ArrayList(examinedUnits);
1281: examinedUnits.retainAll(units);
1282: if (examinedUnits.isEmpty()) {
1283: if (isInfoEnabled())
1284: info("skipping transport task where units don't match "
1285: + units + " vs examined " + copy);
1286: continue;
1287: }
1288:
1289: // THIS IS DONE WHEN WE GET POSSIBLE MATCHES
1290: // are they for the same type of supply?
1291: // if (!contentTypesOverlap (task, examinedTask))
1292: // continue;
1293:
1294: // do the dates overlap
1295: Task reserved, transport;
1296: if (examinedIsReserved) {
1297: reserved = examinedTask;
1298: transport = task;
1299: } else {
1300: reserved = task;
1301: transport = examinedTask;
1302: }
1303:
1304: if (transportDateWithinReservedWindow(transport, reserved)) {
1305: matches.add(examinedTask);
1306: }
1307: }
1308:
1309: return matches;
1310: }
1311:
1312: /*
1313: protected boolean contentTypesOverlap (Task task, Task examinedTask){
1314: Collection typeIDs = getTypesInContainer (task);
1315:
1316: Collection examinedTypeIDs = getTypesInContainer (examinedTask);
1317: Collection copy = new ArrayList (examinedTypeIDs);
1318:
1319: copy.retainAll (typeIDs);
1320: if (copy.isEmpty()) {
1321: if (isDebugEnabled())
1322: debug ("skipping transport task where type ids don't match. " +
1323: "No overlap between examined container " +
1324: examinedTypeIDs +
1325: " and other container's list " + typeIDs);
1326: return false;
1327: }
1328: else {
1329: return true;
1330: }
1331: }
1332: */
1333:
1334: /**
1335: * @return all the ammo types inside the task's milvan
1336: */
1337: protected Collection getTypesInContainer(Task task) {
1338: Container taskDO;
1339: if (task.getDirectObject() instanceof AssetGroup) {
1340: taskDO = (Container) ((AssetGroup) task.getDirectObject())
1341: .getAssets().iterator().next();
1342: } else {
1343: taskDO = (Container) task.getDirectObject();
1344: }
1345: ContentsPG contents = taskDO.getContentsPG();
1346: Collection typeIDs = contents.getTypeIdentifications();
1347: return typeIDs;
1348: }
1349:
1350: /**
1351: * @return a report on all the ammo types inside both tasks' milvan
1352: */
1353: protected String reportContentTypes(Task task, Task examinedTask) {
1354: if (task.getDirectObject() instanceof Container) {
1355: Container taskDO = (Container) task.getDirectObject();
1356: Container examinedDO;
1357: if (examinedTask.getDirectObject() instanceof AssetGroup) {
1358: examinedDO = (Container) ((AssetGroup) examinedTask
1359: .getDirectObject()).getAssets().iterator()
1360: .next();
1361: } else {
1362: examinedDO = (Container) examinedTask.getDirectObject();
1363: }
1364:
1365: ContentsPG contents = taskDO.getContentsPG();
1366: Collection typeIDs = contents.getTypeIdentifications();
1367:
1368: ContentsPG examinedContents = examinedDO.getContentsPG();
1369: Collection examinedTypeIDs = examinedContents
1370: .getTypeIdentifications();
1371:
1372: StringBuffer buf = new StringBuffer();
1373:
1374: buf.append("[");
1375: for (Iterator iter = typeIDs.iterator(); iter.hasNext();)
1376: buf.append(iter.next() + ", ");
1377: buf.append("]");
1378: buf.append(" vs ");
1379: buf.append("[");
1380: for (Iterator iter = examinedTypeIDs.iterator(); iter
1381: .hasNext();)
1382: buf.append(iter.next());
1383: buf.append("]");
1384:
1385: return buf.toString();
1386: } else
1387: return "<not a container>";
1388: }
1389:
1390: /**
1391: * Must check best dates of Supply task parents.
1392: * Fix for bug #12467 and #12468.
1393: *
1394: * The problem is that the packer can aggregate supply tasks for two different ammo types
1395: * into one milvan, and the EARLIER of the two dates becomes the END_TIME best for the
1396: * MP Transport task. It was this time I was using to determine overlap of the Transport
1397: * Reservation that's derived from the ProjectSupply.
1398: *
1399: * E.g. the packer can make:
1400: *
1401: * Parent #1 : C380 arrive at 10/18
1402: * Parent #2 : A986 arrive at 10/15
1403: * -> MPTask with milvan arrive at 10/15 - don't want to make parent #2 be late
1404: *
1405: * So when we try to find the overlap with a Reservation:
1406: * C380 from 9/25->10/20
1407: * if we use the MPTask's arrival time, the overlap results in a Reservation
1408: * from 10/15->10/20 not from 10/18->10/20.
1409: *
1410: * Since the packer may do different packings from run-to-run, the quantities of Reserved
1411: * ammo varies from run to run (and potentially the # of milvans).
1412: *
1413: * @param transport - actual transport task
1414: * @param reserved projected transport task
1415: * @return true if transport task overlaps in time with the reserved task
1416: */
1417: protected boolean transportDateWithinReservedWindow(Task transport,
1418: Task reserved) {
1419: Date reservedReady = (Date) prepHelper.getIndirectObject(
1420: reserved, START);
1421:
1422: Date latestDateOfParents = getLatestParentEndDate(transport,
1423: reserved);
1424:
1425: if (reservedReady.getTime() >= latestDateOfParents.getTime()) {
1426: if (isDebugEnabled()) {
1427: debug("skipping actual transport task where task latest supply parent time "
1428: + latestDateOfParents
1429: + " before examined ready " + reservedReady);
1430: }
1431:
1432: return false;
1433: }
1434:
1435: if (isInfoEnabled()) {
1436: info("transport " + transport.getUID()
1437: + " latest supply parent time "
1438: + latestDateOfParents + " after reserved "
1439: + reserved.getUID() + " ready " + reservedReady);
1440: }
1441:
1442: return true;
1443: }
1444:
1445: /**
1446: * Must check best dates of Supply task parents.
1447: *
1448: * Remember to round to end of day! A task arriving at midnight + 1 sec
1449: * of a day counts for the whole day against the projection.
1450: */
1451: protected Date getLatestParentEndDate(Task transport, Task reserved) {
1452: Collection types = getTypesInContainer(reserved);
1453: if (types.isEmpty()) {
1454: if (isWarnEnabled()) {
1455: warn(getName() + " - huh? no types in container for "
1456: + reserved);
1457: }
1458: }
1459:
1460: // should be only one ammo type in container
1461: String reservedAmmoType = (String) types.iterator().next();
1462: Date latest = getLatestParentEndDate(transport,
1463: reservedAmmoType);
1464:
1465: // round to nearest next day boundary
1466: long round = ((latest.getTime() + MILLIS_PER_DAY) / MILLIS_PER_DAY)
1467: * MILLIS_PER_DAY;
1468:
1469: Date roundDate = new Date(round);
1470:
1471: if (roundDate.getTime() == latest.getTime()) {
1472: if (isWarnEnabled()) {
1473: warn("huh? Didn't move date : date was " + latest
1474: + " now " + roundDate);
1475: }
1476: } else if (isInfoEnabled()) {
1477: info("date was " + latest + " now " + roundDate);
1478: }
1479:
1480: return roundDate;
1481: }
1482:
1483: /**
1484: * Must check best dates of Supply task parents.
1485: */
1486: protected Date getLatestParentEndDate(Task transport,
1487: String reservedAmmoType) {
1488: Date best = prefHelper.getBestDate(transport);
1489: long latestDateOfParents = best.getTime();
1490:
1491: if (transport instanceof MPTask) {
1492: MPTask multiParent = (MPTask) transport;
1493: for (Enumeration en = multiParent.getParentTasks(); en
1494: .hasMoreElements();) {
1495: Task supplyParent = (Task) en.nextElement();
1496:
1497: // check to see parent is of same ammo type
1498: TypeIdentificationPG typePG = supplyParent
1499: .getDirectObject().getTypeIdentificationPG();
1500:
1501: if (typePG == null) { // never happen
1502: if (isWarnEnabled()) {
1503: warn("huh? for task "
1504: + supplyParent
1505: + " the direct object is missing its type PG?");
1506: }
1507: }
1508:
1509: String typeName = typePG.getTypeIdentification();
1510: if (typeName.equals(reservedAmmoType)) {
1511: // check to see if date is later
1512: long parentBest = prefHelper.getBestDate(
1513: supplyParent).getTime();
1514: if (parentBest > latestDateOfParents) {
1515: latestDateOfParents = parentBest;
1516: }
1517: }
1518: }
1519:
1520: return new Date(latestDateOfParents);
1521: } else {
1522: // never used
1523: if (isWarnEnabled()) {
1524: warn("transport task " + transport
1525: + " is not an MPTask?");
1526: }
1527:
1528: return prefHelper.getBestDate(transport);
1529: }
1530: }
1531:
1532: protected void updateMap(Map reservedToActual, Task actual,
1533: Task reserved) {
1534: Task foundActual;
1535: if ((foundActual = (Task) reservedToActual.get(reserved)) == null) {
1536: if (isInfoEnabled()) {
1537: info("initally, actual " + actual.getUID()
1538: + " matches reserved " + reserved.getUID());
1539: }
1540: reservedToActual.put(reserved, actual);
1541: } else {
1542: if (isDebugEnabled()) {
1543: debug("actual " + actual.getUID()
1544: + " matches reserved " + reserved.getUID());
1545: }
1546:
1547: if (prefHelper.getBestDate(foundActual).getTime() < prefHelper
1548: .getBestDate(actual).getTime()) {
1549: if (isInfoEnabled()) {
1550: info("replacing foundActual "
1551: + foundActual.getUID() + " with actual "
1552: + actual.getUID()
1553: + " which matches reserved "
1554: + reserved.getUID());
1555: }
1556: reservedToActual.put(reserved, actual); // replace with later date
1557: }
1558: }
1559: }
1560:
1561: /**
1562: * <pre>
1563: * Called from processTasks.
1564: *
1565: * Compares transport reservation and overlapping actual transport task.
1566: *
1567: * Creates a replacement for existing reserved task, if there is any time
1568: * span left where it doesn't overlap the actual task. If the actual completely
1569: * overlaps the reservation, creates a successful disposition for the task.
1570: *
1571: * If there is partial overlap, adjusts the earliest arrival date on the reserved task
1572: * to be equal to the best date of the actual and updates the contents pg of the direct
1573: * object to indicate a smaller weight.
1574: *
1575: * As a convenience, adds the START prep to the reserved task, indicating the start
1576: * of the period of the reservation. This is used in transportDateWithinReservedWindow to
1577: * determine if an actual falls in the span of a reservation and to indicate whether
1578: * a transport task is indeed a reservation.
1579: *
1580: * </pre>
1581: * @param task actual transport task
1582: * @param reservedTask reserved transport task to be replaced
1583: */
1584: protected void dealWithReservedTask(Task task, Task reservedTask,
1585: Task reservedParent) {
1586: // preconditions
1587: if (isReservedTask(task))
1588: error("arg - task " + task.getUID()
1589: + " is a reserved task.");
1590:
1591: if (!isReservedTask(reservedTask))
1592: error("arg - task " + reservedTask.getUID()
1593: + " is not a reserved task.");
1594:
1595: NewWorkflow tasksWorkflow = (NewWorkflow) reservedTask
1596: .getWorkflow();
1597:
1598: if (tasksWorkflow == null) {
1599: error("huh? reservedTask " + reservedTask.getUID()
1600: + " workflow is null?");
1601: return;
1602: }
1603:
1604: int numTasksBefore = numTasksInWorkflow(tasksWorkflow);
1605:
1606: // real code starts here ---
1607:
1608: // should be only one ammo type in container
1609: Date best = getLatestParentEndDate(task, reservedTask);
1610: Date reservedBest = prefHelper.getBestDate(reservedTask);
1611: long daysLeft = (reservedBest.getTime() - best.getTime())
1612: / MILLIS_PER_DAY;
1613: Date reservedReady = (Date) prepHelper.getIndirectObject(
1614: reservedTask, START);
1615: long currentDays = (reservedBest.getTime() - reservedReady
1616: .getTime())
1617: / MILLIS_PER_DAY;
1618:
1619: if (isInfoEnabled()) {
1620: info(getName()
1621: + ".dealWithReservedTask - applying actual\n"
1622: + task.getUID() + " best " + best
1623: + "\nto reserved\n" + reservedTask.getUID()
1624: + " reserved ready " + reservedReady + " to best "
1625: + reservedBest);
1626: info("\t" + reportContentTypes(task, reservedTask));
1627: }
1628:
1629: double factor = (double) daysLeft / (double) currentDays;
1630: if (factor > 0) { // if the actual doesn't completely cover the period of the projection
1631: Asset deliveredAsset = getTrimmedDirectObject(reservedTask
1632: .getDirectObject(), factor);
1633:
1634: NewTask replacement = (NewTask) expandHelper.makeSubTask(
1635: ldmf, reservedTask.getPlan(), reservedTask
1636: .getParentTaskUID(),
1637: reservedTask.getVerb(), reservedTask
1638: .getPrepositionalPhrases(), deliveredAsset,
1639: reservedTask.getPreferences(), reservedTask
1640: .getPriority(), reservedTask.getSource());
1641: replacement.setContext(reservedTask.getContext());
1642:
1643: if (isInfoEnabled())
1644: info("Reserved task " + reservedTask.getUID()
1645: + " current days " + currentDays + " daysLeft "
1646: + daysLeft + " replacing asset weights.");
1647:
1648: if (isInfoEnabled())
1649: info("on task " + replacement.getUID()
1650: + " replacing start prep date " + reservedReady
1651: + " with " + best
1652: + " - also becomes early date for task.");
1653:
1654: prepHelper.replacePrepOnTask(replacement, prepHelper
1655: .makePrepositionalPhrase(ldmf, START, best));
1656:
1657: if (!FIND_FOR_UNIT_PREP_ON_TASK) {
1658: prepHelper.replacePrepOnTask(replacement, prepHelper
1659: .makePrepositionalPhrase(ldmf,
1660: Constants.Preposition.FOR,
1661: getClusterName()));
1662: }
1663:
1664: prefHelper.replacePreference(replacement, prefHelper
1665: .makeEndDatePreference(ldmf, best, reservedBest,
1666: prefHelper.getLateDate(reservedTask)));
1667:
1668: replacement.setWorkflow(tasksWorkflow);
1669: tasksWorkflow.addTask(replacement);
1670: publishAdd(replacement);
1671: if (isInfoEnabled()) {
1672: info("Publishing replacement " + replacement.getUID()
1673: + " in workflow " + tasksWorkflow.getUID()
1674: + " start " + best + " best " + reservedBest);
1675: }
1676:
1677: if (best.getTime() != ((Date) prepHelper.getIndirectObject(
1678: replacement, START)).getTime()) {
1679: error("replacement start "
1680: + prepHelper.getIndirectObject(replacement,
1681: START) + " != " + best);
1682: }
1683:
1684: if (!taskInWorkflow(replacement, tasksWorkflow)) {
1685: error("huh? after adding to workflow, replacement "
1686: + replacement.getUID() + " is not in workflow "
1687: + tasksWorkflow + "?");
1688: }
1689: } else {
1690: if (isInfoEnabled())
1691: info("Removing reserved task " + reservedTask.getUID()
1692: + " since weight is zero. Days Left was "
1693: + daysLeft + ", current days was "
1694: + currentDays + " parent was "
1695: + reservedTask.getParentTaskUID());
1696: }
1697:
1698: tasksWorkflow.removeTask(reservedTask);
1699: publishRemove(reservedTask);
1700:
1701: if (taskInWorkflow(reservedTask, tasksWorkflow))
1702: error("huh? after removing, reserved task "
1703: + reservedTask.getUID()
1704: + " is still a member of workflow " + tasksWorkflow);
1705:
1706: int numTasksAfter = numTasksInWorkflow(tasksWorkflow);
1707:
1708: if (numTasksAfter == 0) {
1709: if (reservedParent != null) { // I guess if the task is being removed, the parent could be missing from the blackboard
1710: PlanElement exp = reservedParent.getPlanElement();
1711: if (exp == null) {
1712: if (isWarnEnabled()) {
1713: warn("found task " + reservedParent.getUID()
1714: + " verb " + reservedParent.getVerb()
1715: + " that had no plan element.");
1716: }
1717: } else {
1718: publishRemove(exp);
1719: if (isInfoEnabled()) {
1720: info("removing expansion of task "
1721: + exp.getTask().getUID());
1722: }
1723: AllocationResult ar = makeSuccessfulDisposition(reservedParent);
1724: Disposition disposition = ldmf.createDisposition(
1725: reservedParent.getPlan(), reservedParent,
1726: ar);
1727: publishAdd(disposition);
1728: if (isInfoEnabled())
1729: info(" task "
1730: + reservedParent.getUID()
1731: + " verb "
1732: + reservedParent.getVerb()
1733: + " - will get a DISPOSITION, since workflow now empty.");
1734: }
1735: }
1736: }
1737:
1738: if (factor < 0.00000001
1739: && (numTasksAfter != numTasksBefore - 1))
1740: error("Reserved task " + reservedTask.getUID()
1741: + "'s workflow had " + numTasksBefore
1742: + " should have " + (numTasksBefore - 1)
1743: + " but has " + numTasksAfter);
1744: else if (factor > 0 && (numTasksAfter != numTasksBefore))
1745: error("Reserved task " + reservedTask.getUID()
1746: + "'s workflow had " + numTasksBefore
1747: + " != numTaskAfter, which is " + numTasksAfter);
1748: }
1749:
1750: protected boolean isReservedTask(Task task) {
1751: return (prepHelper.hasPrepNamed(task, START));
1752: }
1753:
1754: protected boolean ownWorkflow(Task task) {
1755: if (task.getWorkflow() == null)
1756: return false;
1757:
1758: return taskInWorkflow(task, task.getWorkflow());
1759: }
1760:
1761: protected boolean taskInWorkflow(Task task, Workflow workflow) {
1762: if (workflow == null)
1763: return false;
1764: String[] uidsInWorkflow = ((WorkflowImpl) workflow)
1765: .getTaskIDs();
1766: boolean found = false;
1767: for (int i = 0; i < uidsInWorkflow.length && !found; i++)
1768: if (uidsInWorkflow[i].equals(task.getUID().toString()))
1769: found = true;
1770:
1771: return found;
1772: }
1773:
1774: protected String uidsWorkflow(Task task) {
1775: if (task.getWorkflow() == null) {
1776: return "<null workflow for " + task.getUID() + ">";
1777: }
1778:
1779: return uids(((WorkflowImpl) task.getWorkflow()).getTaskIDs());
1780: }
1781:
1782: protected String uids(String[] array) {
1783: StringBuffer buf = new StringBuffer();
1784: for (int i = 0; i < array.length; i++)
1785: buf.append(array[i] + ", ");
1786: return buf.toString();
1787: }
1788:
1789: protected int numTasksInWorkflow(Workflow workflow) {
1790: int num = 0;
1791: for (Enumeration en = workflow.getTasks(); en.hasMoreElements(); en
1792: .nextElement()) {
1793: num++;
1794: }
1795: return num;
1796: }
1797:
1798: /**
1799: * Makes allocation result with aspect values that echo the task preferences
1800: *
1801: * @param task to dispose
1802: * @return successful allocation result with aspect values taken from task prefs
1803: */
1804: protected AllocationResult makeSuccessfulDisposition(Task task) {
1805: Enumeration prefEnum;
1806: synchronized (task) {
1807: prefEnum = task.getPreferences();
1808: } // bug #2125
1809: List aspectValues = new ArrayList();
1810: while (prefEnum.hasMoreElements()) {
1811: Preference pref = (Preference) prefEnum.nextElement();
1812: ScoringFunction sfunc = pref.getScoringFunction();
1813: aspectValues.add(sfunc.getBest().getAspectValue());
1814: }
1815:
1816: AspectValue[] aspectValueArray = (AspectValue[]) aspectValues
1817: .toArray(new AspectValue[aspectValues.size()]);
1818:
1819: AllocationResult successfulAR = ldmf.newAllocationResult(1.0,
1820: true, aspectValueArray);
1821: return successfulAR;
1822: }
1823:
1824: /**
1825: * Look on task for what unit the ammo is for. Generally this will be 191-ORDN.
1826: *
1827: * If FIND_FOR_UNIT_PREP_ON_TASK is false, just use agent identifier (OSC).
1828: */
1829: protected Collection findForPreps(final Task task) {
1830: List units;
1831: if (FIND_FOR_UNIT_PREP_ON_TASK) {
1832: units = new ArrayList();
1833: if (task instanceof MPTask) {
1834: Collection parents = ((MPTask) task).getComposition()
1835: .getParentTasks();
1836: for (Iterator iter = parents.iterator(); iter.hasNext();) {
1837: Task parentTask = (Task) iter.next();
1838: if (prepHelper.hasPrepNamed(parentTask,
1839: Constants.Preposition.FOR))
1840: units.add(prepHelper.getIndirectObject(
1841: parentTask, Constants.Preposition.FOR));
1842: }
1843: } else {
1844: if (prepHelper.hasPrepNamed(task,
1845: Constants.Preposition.FOR)) {
1846: units.add(prepHelper.getIndirectObject(task,
1847: Constants.Preposition.FOR));
1848: } else {
1849: if (isWarnEnabled())
1850: warn("no FOR prep on task " + task.getUID()
1851: + " using UID owner ");
1852: units.add(task.getUID().getOwner());
1853: }
1854: }
1855:
1856: if (isDebugEnabled())
1857: debug("Units for " + task.getUID() + " were " + units);
1858: } else {
1859: if (defaultUnit == null) {
1860: defaultUnit = new ArrayList();
1861: defaultUnit.add(getClusterName()); // i.e. OSC
1862: }
1863: units = defaultUnit;
1864: }
1865: return units;
1866: }
1867:
1868: /**
1869: * Assumes the direct object is either an asset group or a container (milvan).
1870: * If it's a container, updates the contentsPG to reflect a new weight that is the
1871: * old multiplied by factor (0.0 < factor < 1.0).
1872: *
1873: * Called from dealWithReservedTask.
1874: *
1875: * @param directObject old container or asset group
1876: * @param factor to reduce container weight by
1877: * @return new or old milvan with updated contentsPG
1878: * @see #dealWithReservedTask
1879: */
1880: protected Asset getTrimmedDirectObject(Asset directObject,
1881: double factor) {
1882: if (directObject instanceof AssetGroup) {
1883: double total = 0.0;
1884: Container last = null;
1885:
1886: for (Iterator iter = ((AssetGroup) directObject)
1887: .getAssets().iterator(); iter.hasNext();) {
1888: last = (Container) iter.next();
1889: total += getContainerTons(last);
1890: }
1891:
1892: if (last == null) {
1893: error("Nothing in the asset group of milvans?");
1894: }
1895:
1896: ContentsPG contents = last.getContentsPG();
1897:
1898: String nomen = (String) contents.getNomenclatures()
1899: .iterator().next();
1900: String type = (String) contents.getTypeIdentifications()
1901: .iterator().next();
1902: String unit = (String) contents.getReceivers().iterator()
1903: .next();
1904:
1905: return getMilvanDirectObject(nomen, type, unit, total
1906: * factor);
1907: } else {
1908: Container reserved = (Container) directObject;
1909: ContentsPG contents = reserved.getContentsPG();
1910: Collection weights = contents.getWeights();
1911: Mass weight = (Mass) weights.iterator().next();
1912: weights.remove(weight);
1913: weights.add(new Mass(weight.getKilograms() * factor,
1914: Mass.KILOGRAMS));
1915: return reserved;
1916: }
1917: }
1918:
1919: /** assumes only one type of ammo in container -- true for reservations */
1920: protected double getContainerTons(Container container) {
1921: ContentsPG contents = container.getContentsPG();
1922: Collection weights = contents.getWeights();
1923: Mass weight = (Mass) weights.iterator().next();
1924: return weight.getShortTons();
1925: }
1926: }
|