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.base;
0027:
0028: import org.cougaar.core.adaptivity.OMCRange;
0029: import org.cougaar.core.adaptivity.OMCRangeList;
0030: import org.cougaar.core.adaptivity.OperatingMode;
0031: import org.cougaar.core.adaptivity.OperatingModeImpl;
0032: import org.cougaar.core.agent.service.alarm.Alarm;
0033: import org.cougaar.core.service.BlackboardService;
0034: import org.cougaar.core.service.DomainService;
0035: import org.cougaar.core.service.ThreadService;
0036: import org.cougaar.core.thread.Schedulable;
0037: import org.cougaar.glm.ldm.asset.*;
0038: import org.cougaar.glm.ldm.plan.GeolocLocation;
0039: import org.cougaar.glm.util.AssetUtil;
0040: import org.cougaar.glm.util.GLMPreference;
0041: import org.cougaar.glm.util.GLMPrepPhrase;
0042: import org.cougaar.lib.callback.UTILFilterCallback;
0043: import org.cougaar.lib.callback.UTILFilterCallbackAdapter;
0044: import org.cougaar.lib.callback.UTILFilterCallbackListener;
0045: import org.cougaar.lib.filter.UTILExpanderPluginAdapter;
0046: import org.cougaar.logistics.ldm.Constants;
0047: import org.cougaar.logistics.plugin.trans.CargoCatCodeDimensionPG;
0048: import org.cougaar.logistics.plugin.trans.GLMTransConst;
0049: import org.cougaar.logistics.plugin.trans.LowFidelityAssetPG;
0050: import org.cougaar.logistics.plugin.trans.NewCargoCatCodeDimensionPG;
0051: import org.cougaar.logistics.plugin.trans.NewLowFidelityAssetPG;
0052: import org.cougaar.logistics.plugin.trans.tools.BlackboardPlugin;
0053: import org.cougaar.logistics.plugin.trans.tools.PortLocatorImpl;
0054: import org.cougaar.planning.ldm.PlanningFactory;
0055: import org.cougaar.planning.ldm.asset.AggregateAsset;
0056: import org.cougaar.planning.ldm.asset.Asset;
0057: import org.cougaar.planning.ldm.asset.AssetGroup;
0058: import org.cougaar.planning.ldm.asset.NewItemIdentificationPG;
0059: import org.cougaar.planning.ldm.asset.PropertyGroup;
0060: import org.cougaar.planning.ldm.measure.Area;
0061: import org.cougaar.planning.ldm.measure.Distance;
0062: import org.cougaar.planning.ldm.measure.Latitude;
0063: import org.cougaar.planning.ldm.measure.Longitude;
0064: import org.cougaar.planning.ldm.measure.Mass;
0065: import org.cougaar.planning.ldm.measure.Volume;
0066: import org.cougaar.planning.ldm.plan.AspectType;
0067: import org.cougaar.planning.ldm.plan.Expansion;
0068: import org.cougaar.planning.ldm.plan.NewTask;
0069: import org.cougaar.planning.ldm.plan.PlanElement;
0070: import org.cougaar.planning.ldm.plan.Task;
0071: import org.cougaar.util.UnaryPredicate;
0072: import org.cougaar.util.log.Logger;
0073:
0074: import java.util.ArrayList;
0075: import java.util.Collection;
0076: import java.util.Date;
0077: import java.util.HashSet;
0078: import java.util.Iterator;
0079: import java.util.List;
0080: import java.util.Set;
0081: import java.util.Vector;
0082:
0083: /**
0084: * getSubtasks is filled in. It justs blows up composite
0085: * tasks into smaller one unit tasks. Examines each Task.
0086: *
0087: * @see UTILExpanderPluginAdapter
0088: */
0089: public class GLMTransOneToManyExpanderPlugin extends
0090: UTILExpanderPluginAdapter implements BlackboardPlugin {
0091: /** VTH operating modes */
0092: protected transient OperatingMode level2Horizon, level6Horizon;
0093:
0094: public final Integer LEVEL_2_MIN = new Integer(2); // later, these should be parameters to plugin...
0095: public final Integer LEVEL_2_MAX = new Integer(365);
0096: public final Integer LEVEL_6_MIN = new Integer(1);
0097: public final Integer LEVEL_6_MAX = new Integer(365);
0098: public final int LEVEL_6_MODE = 0;
0099: public final int LEVEL_2_MODE = 1;
0100: /** currently not supported **/
0101: public final int DONT_PROCESS_MODE = 2;
0102: public final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
0103:
0104: public final String LEVEL_2_TIME_HORIZON = "Level2TimeHorizon";
0105: public final Integer LEVEL_2_TIME_HORIZON_DEFAULT = LEVEL_2_MAX;
0106: public final String LEVEL_6_TIME_HORIZON = "Level6TimeHorizon";
0107: public final Integer LEVEL_6_TIME_HORIZON_DEFAULT = LEVEL_6_MAX;
0108:
0109: public final int NUM_TRANSPORT_CLASSES = 9;
0110: public final int ASSET_CLASS_UNKNOWN = 0;
0111: public final int ASSET_CLASS_1 = 1;
0112: public final int ASSET_CLASS_2 = 2;
0113: public final int ASSET_CLASS_3 = 3;
0114: public final int ASSET_CLASS_4 = 4;
0115: public final int ASSET_CLASS_5 = 5;
0116: public final int ASSET_CLASS_6 = 6;
0117: public final int ASSET_CLASS_7 = 7;
0118: public final int ASSET_CLASS_8 = 8;
0119: public final int ASSET_CLASS_9 = 9;
0120: public final int ASSET_CLASS_10 = 10;
0121: public final int ASSET_CLASS_CONTAINER = 11;
0122: public final int ASSET_CLASS_PERSON = 12;
0123: public final long DAY_IN_MILLIS = MILLIS_PER_DAY; //30*1000l;
0124:
0125: public void localSetup() {
0126: super .localSetup();
0127:
0128: try {
0129: myExpandAggregates = (getMyParams()
0130: .hasParam("ExpandAggregates")) ? getMyParams()
0131: .getBooleanParam("ExpandAggregates") : true;
0132: } catch (Exception e) {
0133: warn("got really unexpected exception " + e);
0134: }
0135:
0136: glmPrepHelper = new GLMPrepPhrase(logger);
0137: glmPrefHelper = new GLMPreference(logger);
0138: glmAssetHelper = new AssetUtil(logger);
0139:
0140: setupOperatingModes();
0141: portLocator.setFactory(ldmf); // tell route finder the ldm factory to use
0142: }
0143:
0144: /** create the port locator */
0145: public void setupFilters() {
0146: super .setupFilters();
0147: portLocator = new PortLocatorImpl(this , logger);
0148:
0149: addFilter(modeCallback = new OperatingModeCallback(this , logger));
0150: }
0151:
0152: UTILFilterCallback modeCallback;
0153:
0154: class OperatingModeCallback extends UTILFilterCallbackAdapter {
0155: public OperatingModeCallback(
0156: UTILFilterCallbackListener listener, Logger logger) {
0157: super (listener, logger);
0158: }
0159:
0160: protected UnaryPredicate getPredicate() {
0161: return new UnaryPredicate() {
0162: public boolean execute(Object o) {
0163: boolean val = (o instanceof OperatingMode);
0164: if (val && logger.isInfoEnabled())
0165: logger
0166: .info("GLMTransOneToManyExpanderPlugin.OperatingModeCallback.getPredicate - interested in "
0167: + o);
0168: return val;
0169: }
0170: };
0171: }
0172:
0173: public void reactToChangedFilter() {
0174: if (isInfoEnabled())
0175: info(getName()
0176: + " operating modes sub changed "
0177: + modeCallback.getSubscription()
0178: .getAddedCollection().size()
0179: + " added, "
0180: + modeCallback.getSubscription()
0181: .getChangedCollection().size()
0182: + " changed, "
0183: + modeCallback.getSubscription()
0184: .getRemovedCollection().size()
0185: + " removed");
0186: if (!modeCallback.getSubscription().getChangedCollection()
0187: .isEmpty()) {
0188: if (isInfoEnabled())
0189: info(getName()
0190: + " operating modes changed, so reviewing level 2 tasks.");
0191: reviewLevel2();
0192: if (isInfoEnabled())
0193: info(getName()
0194: + " operating modes changed, so reviewing deferred tasks.");
0195: processTasks(new ArrayList(myInputTaskCallback
0196: .getSubscription().getCollection()));
0197: }
0198: }
0199:
0200: }
0201:
0202: /** create and publish level-2 and 6 VTH Operating Modes */
0203: protected void setupOperatingModes() {
0204: Collection modes = blackboard.query(new UnaryPredicate() {
0205: public boolean execute(Object obj) {
0206: return (obj instanceof OperatingMode);
0207: }
0208: });
0209:
0210: if (modes.size() != 2) {
0211: if (isInfoEnabled())
0212: info(getName()
0213: + " expecting 0 or two operating modes on rehydation - got "
0214: + modes.size() + " instead : " + modes);
0215: }
0216:
0217: // Find the BBoard value for the OpModes, if we don't already have one
0218: for (Iterator iter = modes.iterator(); iter.hasNext();) {
0219: OperatingMode mode = (OperatingMode) iter.next();
0220: if (mode.getName().equals(LEVEL_2_TIME_HORIZON)
0221: && (level2Horizon == null || level2Horizon
0222: .getValue() == null)) {
0223: if (isInfoEnabled())
0224: info(getName()
0225: + " using BBoard value for level2Horizon.");
0226: level2Horizon = mode;
0227: } else if (mode.getName().equals(LEVEL_6_TIME_HORIZON)
0228: && (level6Horizon == null || level6Horizon
0229: .getValue() == null)) {
0230: level6Horizon = mode;
0231: if (isInfoEnabled())
0232: info(getName()
0233: + " using BBoard value for level6Horizon.");
0234: }
0235: }
0236:
0237: // If we now have an OpMode, but it's value is null, it's junk - discard
0238: if (level2Horizon != null && level2Horizon.getValue() == null) {
0239: error(getName() + " had null value for level2Horizon: "
0240: + level2Horizon);
0241: level2Horizon = null;
0242: }
0243:
0244: if (level6Horizon != null && level6Horizon.getValue() == null) {
0245: error(getName() + " had null value for level6Horizon: "
0246: + level6Horizon);
0247: level6Horizon = null;
0248: }
0249:
0250: // If after the BBoard retrieval and error check, we have no OpMode,
0251: // then create and publishAdd it
0252: if (level2Horizon == null) {
0253: OMCRange level2Range = new IntRange(LEVEL_2_MIN.intValue(),
0254: LEVEL_2_MAX.intValue());
0255: OMCRangeList rangeList = new OMCRangeList(level2Range);
0256: publishAdd(level2Horizon = new OperatingModeImpl(
0257: LEVEL_2_TIME_HORIZON, rangeList,
0258: LEVEL_2_TIME_HORIZON_DEFAULT));
0259: if (isInfoEnabled())
0260: info(getAgentIdentifier()
0261: + " created operating mode - "
0262: + "level 2 time horizon is " + level2Horizon);
0263: } else {
0264: if (isInfoEnabled()) {
0265: info(getName()
0266: + " skipping creating of level2Horizon operating mode since got it from blackboard.");
0267: }
0268: }
0269:
0270: if (level6Horizon == null) {
0271: OMCRange level6Range = new IntRange(LEVEL_6_MIN.intValue(),
0272: LEVEL_6_MAX.intValue());
0273: OMCRangeList rangeList = new OMCRangeList(level6Range);
0274: publishAdd(level6Horizon = new OperatingModeImpl(
0275: LEVEL_6_TIME_HORIZON, rangeList,
0276: LEVEL_6_TIME_HORIZON_DEFAULT));
0277: if (isInfoEnabled())
0278: info(getAgentIdentifier()
0279: + " created operating mode - "
0280: + " level 6 horizon is " + level6Horizon);
0281: } else {
0282: if (isInfoEnabled()) {
0283: info(getName()
0284: + " skipping creating of level6Horizon operating mode since got it from blackboard.");
0285: }
0286: }
0287:
0288: }
0289:
0290: protected static class IntRange extends OMCRange {
0291: public IntRange(int a, int b) {
0292: super (a, b);
0293: }
0294: }
0295:
0296: /**
0297: * Implemented for UTILGenericListener interface
0298: *
0299: * Look for tasks that have TRANSPORT as their verb
0300: *
0301: * @param t Task to check for interest
0302: * @return boolean true if task is interesting
0303: */
0304:
0305: public boolean interestingTask(Task t) {
0306: boolean hasTransportVerb = t.getVerb().equals(
0307: Constants.Verb.TRANSPORT);
0308: if (isDebugEnabled() && hasTransportVerb) {
0309: debug(getName() + " : interested in expandable task " + t
0310: + " with direct obj " + t.getDirectObject());
0311: }
0312: if (isDebugEnabled() && !hasTransportVerb)
0313: debug(getName() + " : Ignoring task " + t.getUID()
0314: + " with verb " + t.getVerb());
0315:
0316: return hasTransportVerb;
0317: }
0318:
0319: /**
0320: * <pre>
0321: * Examines task to see if task looks like what the plugin
0322: * expects it to look like.
0323: *
0324: * Checks FROM and TO prepositions to see they're all there.
0325: *
0326: * </pre>
0327: * @param taskToCheck Task to check for consistency
0328: * @return true if task is OK
0329: */
0330: public boolean isTaskWellFormed(Task taskToCheck) {
0331: boolean taskTiming = verifyHelper
0332: .isTaskTimingCorrect(taskToCheck);
0333:
0334: if (!taskTiming) {
0335: reportError(taskToCheck.getUID()
0336: + " failed timing constraint check - e.g. is earliest arrival before start?");
0337: return false;
0338: }
0339:
0340: if (glmPrepHelper == null)
0341: error(getName()
0342: + ".isTaskWellFormed - huh? glmPrepHelper is null??");
0343:
0344: GeolocLocation start = (GeolocLocation) glmPrepHelper
0345: .getIndirectObject(taskToCheck,
0346: Constants.Preposition.FROM);
0347: GeolocLocation end = (GeolocLocation) glmPrepHelper
0348: .getIndirectObject(taskToCheck,
0349: Constants.Preposition.TO);
0350:
0351: if (start == null) {
0352: reportError(".isTaskWellFormed : Hey! For task "
0353: + taskToCheck + " FROM geoloc is null!");
0354: return false;
0355: } else if (end == null) {
0356: reportError(".isTaskWellFormed : Hey! For task "
0357: + taskToCheck + " TO geoloc is null!");
0358: return false;
0359: } else {
0360: Longitude lon = start.getLongitude();
0361: Latitude lat = start.getLatitude();
0362: if (lon == null) {
0363: reportError(".isTaskWellFormed : For task "
0364: + taskToCheck + "\n FROM longitude is null!");
0365: return false;
0366: } else if (isDebugEnabled())
0367: debug(".isTaskWellFormed\n\tTask : " + taskToCheck
0368: + "\nstart (long, lat) is (" + lon.getDegrees()
0369: + "," + lat.getDegrees() + ")");
0370:
0371: lon = end.getLongitude();
0372: lat = end.getLatitude();
0373: if (lon == null) {
0374: reportError(".isTaskWellFormed : TO longitude is null!");
0375: return false;
0376: } else if (isDebugEnabled())
0377: debug("end (long, lat) is (" + lon.getDegrees() + ","
0378: + lat.getDegrees() + ")");
0379:
0380: // if (isInfoEnabled())
0381: // info ("Distance between is : " +
0382: // UTILUtil.distanceBetween (start, end).getMiles () + " miles.");
0383: }
0384:
0385: return true;
0386: }
0387:
0388: public void processTasks(java.util.List tasks) {
0389: super .processTasks(getPrunedTaskList(tasks));
0390:
0391: if (logger.isDebugEnabled()) {
0392: logger.debug("Finished processing " + tasks.size()
0393: + " tasks at "
0394: + new Date(alarmService.currentTimeMillis())
0395: + " Cougaar Time " + new Date() + " clock time.");
0396: }
0397: }
0398:
0399: protected List getPrunedTaskList(List tasks) {
0400: java.util.List prunedTasks = new java.util.ArrayList(tasks
0401: .size());
0402:
0403: Collection removed = myInputTaskCallback.getSubscription()
0404: .getRemovedCollection();
0405:
0406: for (Iterator iter = tasks.iterator(); iter.hasNext();) {
0407: Task task = (Task) iter.next();
0408: if (removed.contains(task)) {
0409: if (isInfoEnabled()) {
0410: info("ignoring task on removed list "
0411: + task.getUID());
0412: }
0413: } else
0414: prunedTasks.add(task);
0415: }
0416: return prunedTasks;
0417: }
0418:
0419: public void handleTask(Task parentTask) {
0420: int mode = getMode(parentTask);
0421: if (isDebugEnabled()) {
0422: debug(".getSubtasks - mode for task "
0423: + parentTask.getUID()
0424: + " is "
0425: + ((mode == LEVEL_6_MODE) ? "LEVEL_6"
0426: : ((mode == LEVEL_2_MODE) ? "LEVEL_2"
0427: : "DONT_PROCESS")));
0428: }
0429:
0430: if (parentTask.getPlanElement() != null) { // shouldn't it have been removed from my collection???
0431: if (isInfoEnabled()) {
0432: info(getName()
0433: + ".getSubtasks - skipping previously planned task "
0434: + parentTask.getUID() + ".");
0435: }
0436: return;
0437: }
0438:
0439: if (mode != LEVEL_6_MODE) {
0440: synchronized (alarmMutex) { // alarm.expire will try to clear currentAlarm so must synchronize on it
0441: if (currentAlarm == null) {
0442: startAgainIn(DAY_IN_MILLIS);
0443: } else {
0444: if (isDebugEnabled()) {
0445: debug(getName()
0446: + ".getSubtasks - not starting new alarm.");
0447: }
0448: }
0449: }
0450:
0451: if (mode == DONT_PROCESS_MODE) {
0452: if (isInfoEnabled()) {
0453: info(getName()
0454: + ".getSubtasks - not processing "
0455: + parentTask.getUID()
0456: + " for now (= "
0457: + new Date(alarmService.currentTimeMillis())
0458: + "), will revisit at "
0459: + new Date(currentAlarm.getExpirationTime()));
0460: }
0461: } else {
0462: if (isInfoEnabled()) {
0463: info(getName()
0464: + ".getSubtasks - processing "
0465: + parentTask.getUID()
0466: + " at level 2 for now (= "
0467: + new Date(alarmService.currentTimeMillis())
0468: + "), will revisit at "
0469: + new Date(currentAlarm.getExpirationTime()));
0470: }
0471: super .handleTask(parentTask);
0472: }
0473:
0474: return;
0475: } else
0476: super .handleTask(parentTask);
0477: }
0478:
0479: int i = 0;
0480:
0481: /**
0482: * <pre>
0483: * Implemented for UTILExpanderPlugin interface
0484: *
0485: * Break up tasks into constituent parts.
0486: *
0487: * There are three possible paths a task can take:
0488: *
0489: * 1) If the task is already a LEVEL 2 task, attaches a lowFiAssetPG to the asset and
0490: * passes it through.
0491: * 2) If the task's dates and the operating mode VTH determine it should be handled
0492: * in level-2 mode, create a level-2 task from the original
0493: * 3) If the task should be handled in level-6, expand it's d.o. as usual
0494: * 4) TBD - perhaps, in the future, we'll ignore tasks that are far into the future
0495: * until the current time advances far enough. This would require the buffering thread
0496: * to wake up periodically, however, using the alarm service. Hmmm...
0497: *
0498: * There is also the possibility of rescinding level 2 and replanning as level 6.
0499: * That would be real work though.
0500: * </pre>
0501: * @return Vector of subtasks of parentTask
0502: */
0503: public Vector getSubtasks(Task parentTask) {
0504: Vector childTasks = new Vector();
0505:
0506: if (isInfoEnabled())
0507: info(".getSubtasks - received task " + (i++) + " total.");
0508:
0509: int mode = getMode(parentTask);
0510: if (isDebugEnabled()) {
0511: debug(".getSubtasks - mode for task "
0512: + parentTask.getUID()
0513: + " is "
0514: + ((mode == LEVEL_6_MODE) ? "LEVEL_6"
0515: : ((mode == LEVEL_2_MODE) ? "LEVEL_2"
0516: : "DONT_PROCESS")));
0517: }
0518:
0519: // if the incoming task is already a low fidelity task, we don't need to do anything to it
0520: if (prepHelper.hasPrepNamed(parentTask,
0521: GLMTransConst.LOW_FIDELITY)) {
0522: // pass through
0523: Asset lowFiAsset = parentTask.getDirectObject();
0524: Task subTask = makeTask(parentTask, lowFiAsset, null);
0525: NewLowFidelityAssetPG lowFiPG = (NewLowFidelityAssetPG) ldmf
0526: .createPropertyGroup(LowFidelityAssetPG.class);
0527: lowFiPG.setOriginalAsset(lowFiAsset);
0528: attachPG(lowFiAsset, lowFiPG); // now subobject has pointer back to parent
0529: if (isDebugEnabled())
0530: debug(".getSubtasks - processing task "
0531: + parentTask.getUID()
0532: + " by attaching low fi pg to it's d.o. - "
0533: + lowFiAsset);
0534: childTasks.addElement(subTask);
0535: } else if ((getMode(parentTask) == LEVEL_2_MODE)
0536: && ((parentTask.getDirectObject() instanceof AssetGroup) || // it doesn't make sense to aggregate an individual item asset
0537: (parentTask.getDirectObject() instanceof AggregateAsset))
0538: && !isPersonTask(parentTask)) {
0539: if (isDebugEnabled())
0540: debug(".getSubtasks - processing task "
0541: + parentTask.getUID()
0542: + " in LOW fidelity mode. d.o. is "
0543: + parentTask.getDirectObject());
0544:
0545: Set[] categories = sortAssetsByCategory(expandAsset(
0546: parentTask, parentTask.getDirectObject()));
0547:
0548: for (int i = 0; i < NUM_TRANSPORT_CLASSES; i++) {
0549: if (!categories[i].isEmpty())
0550: childTasks.add(getLowFidelityTask(parentTask,
0551: categories[i]));
0552: }
0553: } else {
0554: if (isDebugEnabled())
0555: debug(".getSubtasks - processing task "
0556: + parentTask.getUID()
0557: + " in HIGH fidelity mode. d.o. is "
0558: + parentTask.getDirectObject());
0559:
0560: Vector itemsToMove = expandAsset(parentTask,
0561: (Asset) parentTask.getDirectObject());
0562:
0563: for (int i = 0; i < itemsToMove.size(); i++) {
0564: Task subTask = makeTask(parentTask, (Asset) itemsToMove
0565: .elementAt(i), null);
0566: childTasks.addElement(subTask);
0567: }
0568: }
0569:
0570: return childTasks;
0571: }
0572:
0573: /**
0574: * Decide the mode the task should be processed in, depending on when it asks to be done,
0575: * and comparing that time against the current time and the level 2 and 6 time horizons
0576: *
0577: * @return either LEVEL_6_MODE, LEVEL_2_MODE, or DONT_PROCESS_MODE (currently not supported)
0578: */
0579: protected int getMode(Task parentTask) {
0580: long bestTime = prefHelper.getBestDate(parentTask).getTime();
0581: long currentTime = getAlarmService().currentTimeMillis();
0582:
0583: // Double check -- if we have no good OpModes here, then get them
0584: if (level6Horizon == null || level2Horizon == null
0585: || level6Horizon.getValue() == null
0586: || level2Horizon.getValue() == null) {
0587: // create them
0588: setupOperatingModes();
0589: }
0590: long level6Day = ((Integer) level6Horizon.getValue())
0591: .longValue();
0592: long level2Day = ((Integer) level2Horizon.getValue())
0593: .longValue();
0594: if (bestTime < currentTime + level6Day * MILLIS_PER_DAY) {
0595: return LEVEL_6_MODE;
0596: } else if (bestTime < currentTime + level2Day * MILLIS_PER_DAY) {
0597: if (isInfoEnabled())
0598: info(getName() + ".getMode - got level 2 task ("
0599: + parentTask.getUID() + ")\nsince best "
0600: + new java.util.Date(bestTime)
0601: + " is after current "
0602: + new java.util.Date(currentTime)
0603: + " + level6 days " + level6Day);
0604: return LEVEL_2_MODE;
0605: }
0606: return DONT_PROCESS_MODE;
0607: }
0608:
0609: protected Set[] sortAssetsByCategory(Collection assets) {
0610: Set[] categories = new Set[NUM_TRANSPORT_CLASSES];
0611:
0612: categories[0] = new HashSet();
0613: categories[1] = new HashSet();
0614: categories[2] = new HashSet();
0615: categories[3] = new HashSet();
0616: categories[4] = new HashSet();
0617: categories[5] = new HashSet();
0618: categories[6] = new HashSet();
0619: categories[7] = new HashSet();
0620: categories[8] = new HashSet();
0621:
0622: for (Iterator iter = assets.iterator(); iter.hasNext();) {
0623: GLMAsset asset = (GLMAsset) iter.next();
0624: String ccc = getCategory(asset);
0625:
0626: switch (ccc.charAt(1)) {
0627: case '0': // non-air
0628: if (ccc.charAt(0) == 'R') {
0629: categories[4].add(asset);
0630: } else {
0631: categories[0].add(asset);
0632: }
0633: break;
0634: case '1': // outsized
0635: if (ccc.charAt(0) == 'R') {
0636: categories[5].add(asset);
0637: } else {
0638: categories[1].add(asset);
0639: }
0640: break;
0641: case '2': // oversized
0642: if (ccc.charAt(0) == 'R') {
0643: categories[6].add(asset);
0644: } else {
0645: categories[2].add(asset);
0646: }
0647: break;
0648: default: // bulk or unknown
0649: if (asset instanceof Container) {
0650: categories[8].add(asset);
0651: } else if (ccc.charAt(0) == 'R') {
0652: categories[7].add(asset);
0653: } else {
0654: categories[3].add(asset);
0655: }
0656: break;
0657: }
0658: }
0659:
0660: return categories;
0661: }
0662:
0663: /** create level 2 task by aggregating the contents of the direct object **/
0664: protected Task getLowFidelityTask(Task parentTask, Set uniformAssets) {
0665: Task newTask = null;
0666:
0667: GLMAsset lowFiAsset = null;
0668: NewLowFidelityAssetPG lowFiPG = (NewLowFidelityAssetPG) ldmf
0669: .createPropertyGroup(LowFidelityAssetPG.class);
0670: NewMovabilityPG movabilityPG = (NewMovabilityPG) ldmf
0671: .createPropertyGroup(MovabilityPG.class);
0672:
0673: try {
0674: NewPhysicalPG newPhysicalPG = PropertyGroupFactory
0675: .newPhysicalPG();
0676: CargoCatCodeDimensionPG cccd = setDimensions(parentTask,
0677: newPhysicalPG, uniformAssets);
0678: lowFiPG.setCCCDim(cccd);
0679:
0680: lowFiAsset = (GLMAsset) assetHelper.createInstance(
0681: getLDMService().getLDM(), "Level2Prototype",
0682: "Level2_" + getTransportType(cccd) + "_"
0683: + getNextID());
0684: ((NewItemIdentificationPG) lowFiAsset
0685: .getItemIdentificationPG())
0686: .setNomenclature("Level2Aggregate");
0687:
0688: lowFiPG.setOriginalAsset(lowFiAsset); // now subobject can have a pointer back to parent
0689:
0690: lowFiAsset.setPhysicalPG(newPhysicalPG);
0691: lowFiAsset.addOtherPropertyGroup(lowFiPG);
0692: lowFiAsset.setMovabilityPG(movabilityPG);
0693:
0694: movabilityPG.setCargoCategoryCode(cccd.getCargoCatCode());
0695:
0696: if (!movabilityPG.getCargoCategoryCode().equals(
0697: cccd.getCargoCatCode()))
0698: logger.error("huh? for asset "
0699: + lowFiAsset
0700: + " on task "
0701: + parentTask.getUID()
0702: + " movabilityPG ccc "
0703: + lowFiAsset.getMovabilityPG()
0704: .getCargoCategoryCode() + " != "
0705: + cccd.getCargoCatCode());
0706:
0707: } catch (Exception e) {
0708: Asset asset = parentTask.getDirectObject();
0709: logger.error("problem processing task "
0710: + parentTask.getUID() + "'s d.o. asset" + asset, e);
0711: return null;
0712: }
0713:
0714: if (isDebugEnabled())
0715: debug("getLowFidelityTask - created low fi asset "
0716: + lowFiAsset.getUID()
0717: + " : "
0718: + lowFiAsset.getItemIdentificationPG()
0719: .getNomenclature()
0720: + " - "
0721: + lowFiAsset.getItemIdentificationPG()
0722: .getItemIdentification());
0723:
0724: newTask = makeTask(parentTask, lowFiAsset,
0725: getOriginalOwner(parentTask));
0726:
0727: // mark the new task as an aggregate low fi task
0728: prepHelper.addPrepToTask(newTask, prepHelper
0729: .makePrepositionalPhrase(ldmf,
0730: GLMTransConst.LOW_FIDELITY,
0731: GLMTransConst.LOW_FIDELITY));
0732:
0733: return newTask;
0734: }
0735:
0736: /** return a human-readable string to indicate transport type */
0737: protected String getTransportType(CargoCatCodeDimensionPG cccd) {
0738: String ccc = cccd.getCargoCatCode();
0739: String suffix = (ccc.charAt(0) == 'R') ? "_Roadable" : "";
0740:
0741: switch (ccc.charAt(1)) {
0742: case '0': // non-air
0743: return "Non-air Transport" + suffix;
0744: case '1': // outsized
0745: return "Outsized" + suffix;
0746: case '2': // oversized
0747: return "Oversized" + suffix;
0748: default: // bulk or unknown
0749: if (cccd.getIsContainer()) {
0750: return "Container" + suffix;
0751: } else {
0752: return "Bulk" + suffix;
0753: }
0754: }
0755: }
0756:
0757: /**
0758: * Recovers owner of task's d.o. from either a FOR prep on the task
0759: * or the owner part of UID of the d.o.
0760: * @param parentTask task to examine
0761: * @return owner of the task = which unit sent the task, owns the asset
0762: */
0763: protected String getOriginalOwner(Task parentTask) {
0764: if (!glmPrepHelper.hasPrepNamed(parentTask,
0765: Constants.Preposition.FOR)) {
0766: Asset directObject = parentTask.getDirectObject();
0767: String owner = directObject.getUID().getOwner();
0768:
0769: if (isInfoEnabled()) {
0770: info(".getOriginalOwner - WARNING : got task "
0771: + parentTask.getUID()
0772: + " which has no FOR unit prep, using owner - "
0773: + owner + ".");
0774: }
0775:
0776: return owner;
0777: } else
0778: return (String) glmPrepHelper.getIndirectObject(parentTask,
0779: Constants.Preposition.FOR);
0780: }
0781:
0782: /**
0783: * Since FOR preps are lost a custom property is added to determine unit
0784: *
0785: * @param asset to attach PG to
0786: * @param thisPG pg to attach to asset
0787: */
0788: public void attachPG(Asset asset, PropertyGroup this PG) {
0789: if (asset instanceof AssetGroup) {
0790: Vector assetList = ((AssetGroup) asset).getAssets();
0791: for (int i = 0; i < assetList.size(); i++) {
0792: attachPG((Asset) assetList.elementAt(i), this PG);
0793: }
0794: } else if (asset instanceof AggregateAsset) {
0795: // Put in both because unsure of behavior
0796: asset.addOtherPropertyGroup(this PG);
0797: // Don't want to do this, since every aggregate of X
0798: //XX asset will then have this pg
0799: // attachUnitPG(((AggregateAsset)asset).getAsset(),unitPG);
0800: } else {
0801: asset.addOtherPropertyGroup(this PG);
0802: }
0803: }
0804:
0805: /**
0806: * <pre>
0807: * Tries to determine if direct object is a person aggregate.
0808: *
0809: * Can be tricky since sometimes the d.o. is a group of aggregates.
0810: * </pre>
0811: */
0812: protected boolean isPersonTask(Task parentTask) {
0813: Asset asset = parentTask.getDirectObject();
0814: if (asset instanceof AggregateAsset) {
0815: GLMAsset itemProto = (GLMAsset) ((AggregateAsset) asset)
0816: .getAsset();
0817: return itemProto.hasPersonPG();
0818: }
0819: // if aggregate assets of people are inside of an asset group
0820: // it's a person...
0821: else if (asset instanceof AssetGroup) {
0822: AssetGroup group = (AssetGroup) asset;
0823: Vector assetList = group.getAssets();
0824: for (int i = 0; i < assetList.size(); i++) {
0825: Asset subasset = (Asset) assetList.elementAt(i);
0826: if (subasset instanceof AggregateAsset) {
0827: GLMAsset itemProto = (GLMAsset) ((AggregateAsset) subasset)
0828: .getAsset();
0829: return itemProto.hasPersonPG();
0830: } else
0831: return false;
0832: }
0833: }
0834: return false;
0835: }
0836:
0837: protected int id = 0;
0838:
0839: protected int getNextID() {
0840: return id++;
0841: }
0842:
0843: /**
0844: * <pre>
0845: * Takes a collection of assets and sets the dimensions of the physical pg
0846: * to be their sum, in area, volume, and weight.
0847: *
0848: * It doesn't make sense to aggregate length, width, and height, since they
0849: * wouldn't correspond to area and volume.
0850: * </pre>
0851: * @param realAssets to sum
0852: * @param physicalPG to set with their aggregate dimensions
0853: **/
0854: protected CargoCatCodeDimensionPG setDimensions(Task parentTask,
0855: NewPhysicalPG physicalPG, Collection realAssets) {
0856: double area = 0.0;
0857: double volume = 0.0;
0858: double mass = 0.0;
0859:
0860: Object firstItem = realAssets.iterator().next();
0861:
0862: PhysicalPG cccdPhysicalPG = (PhysicalPG) ldmf
0863: .createPropertyGroup(PhysicalPG.class);
0864: NewCargoCatCodeDimensionPG cccdPG = (NewCargoCatCodeDimensionPG) ldmf
0865: .createPropertyGroup(CargoCatCodeDimensionPG.class);
0866: cccdPG.setDimensions(cccdPhysicalPG);
0867:
0868: if (firstItem instanceof AggregateAsset) { // there is only one item...
0869: if (realAssets.size() > 1)
0870: logger
0871: .error(getAgentIdentifier()
0872: + " found aggregate, but skipping some expanded items???");
0873:
0874: AggregateAsset aggAsset = (AggregateAsset) firstItem;
0875: GLMAsset baseAsset = (GLMAsset) aggAsset.getAsset();
0876: PhysicalPG itemPhysicalPG = baseAsset.getPhysicalPG();
0877: String ccc = getCategory(baseAsset);
0878:
0879: if (itemPhysicalPG == null) {
0880: if (!baseAsset.hasPersonPG()) {
0881: warn(".setDimensions - for task "
0882: + parentTask.getUID() + " asset "
0883: + firstItem + "'s base asset "
0884: + (GLMAsset) aggAsset.getAsset()
0885: + " has no physical PG.");
0886: } else if (isDebugEnabled()) {
0887: debug(".setDimensions - NOTE : asset " + firstItem
0888: + "'s base asset "
0889: + (GLMAsset) aggAsset.getAsset()
0890: + " has no physical PG.");
0891: }
0892: } else {
0893: long quantity = aggAsset.getQuantity();
0894: double q = (double) quantity;
0895:
0896: // it doesn't make sense to display aggregate length, width, height,
0897: // since they won't correspond to area and volume
0898: area = itemPhysicalPG.getFootprintArea()
0899: .getSquareMeters()
0900: * q;
0901: volume = itemPhysicalPG.getVolume().getCubicMeters()
0902: * q;
0903: mass = itemPhysicalPG.getMass().getKilograms() * q;
0904:
0905: if (area == 0.0d) {
0906: area = itemPhysicalPG.getLength().getMeters()
0907: * itemPhysicalPG.getWidth().getMeters() * q;
0908: if (isInfoEnabled()) {
0909: info(".setDimensions - fixing footprint area, was zero for asset "
0910: + aggAsset
0911: + " on task "
0912: + parentTask.getUID());
0913: }
0914: }
0915: if (area == 0.0d || volume == 0.0d || mass == 0.0d) {
0916: if (isWarnEnabled()) {
0917: warn(".setDimensions - asset " + firstItem
0918: + " for task " + parentTask.getUID()
0919: + " has a zero dimension.");
0920: }
0921: }
0922:
0923: addToDimension(ccc, q, itemPhysicalPG, cccdPG);
0924:
0925: if (baseAsset instanceof Container)
0926: cccdPG.setIsContainer(true);
0927: cccdPG.setAssetClass(getAssetClass(baseAsset));
0928: }
0929: } else {
0930: Object last = null;
0931: for (Iterator iter = realAssets.iterator(); iter.hasNext();) {
0932: GLMAsset asset = (GLMAsset) iter.next();
0933: last = asset;
0934: PhysicalPG itemPhysicalPG = asset.getPhysicalPG();
0935: String ccc = getCategory(asset);
0936:
0937: if (itemPhysicalPG == null)
0938: error("Asset " + asset
0939: + " doesn't have a physical PG.");
0940: else {
0941: // it doesn't make sense to display aggregate length, width, height,
0942: // since they won't correspond to area and volume
0943: double itemArea = itemPhysicalPG.getFootprintArea()
0944: .getSquareMeters();
0945: double itemVolume = itemPhysicalPG.getVolume()
0946: .getCubicMeters();
0947: double itemMass = itemPhysicalPG.getMass()
0948: .getKilograms();
0949:
0950: if (itemArea < 0.01d) {
0951: itemArea = itemPhysicalPG.getLength()
0952: .getMeters()
0953: * itemPhysicalPG.getWidth().getMeters();
0954: if (isInfoEnabled()) {
0955: info(".setDimensions - fixing footprint area, was zero for asset "
0956: + asset
0957: + " on task "
0958: + parentTask.getUID());
0959: }
0960: }
0961:
0962: area += itemArea;
0963: volume += itemVolume;
0964: mass += itemMass;
0965:
0966: if (itemArea == 0.0d || itemVolume == 0.0d
0967: || itemMass == 0.0d) {
0968: if (isWarnEnabled()) {
0969: warn(".setDimensions - asset " + asset
0970: + " for task "
0971: + parentTask.getUID()
0972: + " has a zero dimension.");
0973: }
0974: }
0975:
0976: addToDimension(ccc, 1.0, itemPhysicalPG, cccdPG);
0977: }
0978: }
0979: if (last instanceof Container)
0980: cccdPG.setIsContainer(true);
0981: cccdPG.setAssetClass(getAssetClass((Asset) last));
0982: }
0983:
0984: physicalPG.setMass(new Mass(mass, Mass.KILOGRAMS));
0985: physicalPG.setFootprintArea(new Area(area, Area.SQUARE_METERS));
0986: physicalPG.setVolume(new Volume(volume, Volume.CUBIC_METERS));
0987:
0988: if (isDebugEnabled()) {
0989: debug(".setDimensions got " + realAssets.size() + " items.");
0990: debug(".setDimensions low fi dimensions : " + " m "
0991: + physicalPG.getMass() + " a "
0992: + physicalPG.getFootprintArea() + " v "
0993: + physicalPG.getVolume());
0994: }
0995:
0996: return cccdPG;
0997: }
0998:
0999: protected String getCategory(GLMAsset asset) {
1000: MovabilityPG movabilityPG = asset.getMovabilityPG();
1001:
1002: String ccc;
1003:
1004: if (movabilityPG == null) {
1005: ccc = "XXX";
1006: logger
1007: .warn(getAgentIdentifier()
1008: + " "
1009: + asset
1010: + " was missing a movability PG, so could not determine cargo cat code.");
1011: } else {
1012: ccc = movabilityPG.getCargoCategoryCode();
1013: if (logger.isDebugEnabled())
1014: logger.debug(asset.toString() + " ccc was " + ccc);
1015: }
1016:
1017: return ccc;
1018: }
1019:
1020: protected void addToDimension(String ccc, double quantity,
1021: PhysicalPG itemPhysicalPG, CargoCatCodeDimensionPG cccdPG) {
1022: NewPhysicalPG whichPG = (NewPhysicalPG) cccdPG.getDimensions();
1023: NewCargoCatCodeDimensionPG whichCCCDimPG = (NewCargoCatCodeDimensionPG) cccdPG;
1024:
1025: if (whichPG == null) {
1026: logger.error("Could not set physical PG?");
1027: }
1028:
1029: String pgCCCString = whichCCCDimPG.getCargoCatCode();
1030:
1031: if (pgCCCString == null) { // we've never set the cargo cat code
1032: whichCCCDimPG.setCargoCatCode(ccc);
1033: } else if (!pgCCCString.equals(ccc)) {
1034: char[] pgCCC = pgCCCString.toCharArray();
1035: char[] newCCC = new char[3];
1036: char[] cccArray = ccc.toCharArray();
1037:
1038: if (isDebugEnabled())
1039: debug(".addToDimension old " + new String(pgCCC)
1040: + " additional ccc " + ccc);
1041:
1042: if (cccArray[0] != pgCCC[0]) {
1043: newCCC[0] = 'X';
1044: } else {
1045: newCCC[0] = pgCCC[0];
1046: }
1047:
1048: if (cccArray[1] != pgCCC[1]) {
1049: newCCC[1] = 'X';
1050: } else {
1051: newCCC[1] = pgCCC[1];
1052: }
1053:
1054: if (cccArray[2] != pgCCC[2]) {
1055: newCCC[2] = 'X';
1056: } else {
1057: newCCC[2] = pgCCC[2];
1058: }
1059:
1060: whichCCCDimPG.setCargoCatCode(new String(newCCC));
1061: }
1062:
1063: double area = 0.0d;
1064: double volume = 0.0d;
1065: double mass = 0.0d;
1066:
1067: if (whichPG.getFootprintArea() != null) {
1068: area = whichPG.getFootprintArea().getSquareMeters();
1069: /*
1070: if (area < 0.01d) {
1071: area = whichPG.getLength().getMeters() * whichPG.getWidth().getMeters();
1072: if (isInfoEnabled ()) {
1073: info (".addToDimensions - fixing footprint area, was zero for asset");
1074: }
1075: }
1076: */
1077: volume = whichPG.getVolume().getCubicMeters();
1078: mass = whichPG.getMass().getKilograms();
1079: }
1080:
1081: whichPG.setFootprintArea(new Area(area
1082: + itemPhysicalPG.getFootprintArea().getSquareMeters()
1083: * quantity, Area.SQUARE_METERS));
1084: whichPG.setVolume(new Volume(volume
1085: + itemPhysicalPG.getVolume().getCubicMeters()
1086: * quantity, Volume.CUBIC_METERS));
1087: whichPG.setMass(new Mass(mass
1088: + itemPhysicalPG.getMass().getKilograms() * quantity,
1089: Mass.KILOGRAMS));
1090:
1091: if (isDebugEnabled())
1092: debug(".addToDimension final dim for " + ccc + " - "
1093: + whichPG.getFootprintArea().getSquareMeters()
1094: + " m^2" + whichPG.getVolume().getCubicMeters()
1095: + " m^3 " + whichPG.getMass().getShortTons()
1096: + " short tons.");
1097: }
1098:
1099: private int getAssetClass(Asset a) {
1100: int value = (a instanceof ClassISubsistence) ? ASSET_CLASS_1
1101: : (a instanceof ClassIIClothingAndEquipment) ? ASSET_CLASS_2
1102: : (a instanceof ClassIIIPOL) ? ASSET_CLASS_3
1103: : (a instanceof ClassIVConstructionMaterial) ? ASSET_CLASS_4
1104: : (a instanceof ClassVAmmunition) ? ASSET_CLASS_5
1105: : (a instanceof ClassVIPersonalDemandItem) ? ASSET_CLASS_6
1106: : (a instanceof ClassVIIMajorEndItem) ? ASSET_CLASS_7
1107: : (a instanceof ClassVIIIMedical) ? ASSET_CLASS_8
1108: : (a instanceof ClassIXRepairPart) ? ASSET_CLASS_9
1109: : (a instanceof ClassXNonMilitaryItem) ? ASSET_CLASS_10
1110: : ((a instanceof Container) || (a instanceof org.cougaar.glm.ldm.asset.Package)// ||
1111: // (glmAssetHelper.isPallet(a))) ?
1112: ) ? ASSET_CLASS_CONTAINER
1113: : ((a instanceof Person) || ((a instanceof GLMAsset) && (((GLMAsset) a)
1114: .hasPersonPG()))) ? ASSET_CLASS_PERSON
1115: : ASSET_CLASS_UNKNOWN;
1116:
1117: return value;
1118: }
1119:
1120: /**
1121: * Makes subtask of parent task, with given direct object.
1122: *
1123: * removes OFTYPE prep, since it's not needed by scheduler
1124: **/
1125: protected Task makeTask(Task parentTask, Asset directObject,
1126: String originalOwner) {
1127: if (isDebugEnabled())
1128: debug(".makeTask - making subtask of "
1129: + parentTask.getUID() + " d.o. " + directObject);
1130:
1131: Task newtask = expandHelper.makeSubTask(ldmf, parentTask,
1132: directObject, getAgentIdentifier());
1133: ((NewTask) newtask).setContext(parentTask.getContext());
1134: glmPrepHelper.removePrepNamed(newtask,
1135: Constants.Preposition.OFTYPE);
1136:
1137: // Next four lines create a Property Group for unit and attach it to all assets attached to task
1138: ForUnitPG unitPG = (ForUnitPG) ldmf
1139: .createPropertyGroup(ForUnitPG.class);
1140: if (!glmPrepHelper.hasPrepNamed(newtask,
1141: Constants.Preposition.FOR)) {
1142: String owner = (originalOwner != null) ? originalOwner
1143: : directObject.getUID().getOwner();
1144:
1145: if (isInfoEnabled()) {
1146: info(".getSubtasks - NOTE : got task "
1147: + parentTask.getUID()
1148: + " which has no FOR unit prep, using owner - "
1149: + owner + ".");
1150: }
1151:
1152: ((NewForUnitPG) unitPG).setUnit(owner);
1153: } else {
1154: ((NewForUnitPG) unitPG).setUnit((String) glmPrepHelper
1155: .getIndirectObject(newtask,
1156: Constants.Preposition.FOR));
1157: glmPrepHelper.removePrepNamed(newtask,
1158: Constants.Preposition.FOR);
1159: }
1160:
1161: attachPG(directObject, unitPG);
1162:
1163: // glmtrans doesn't deal with quantity -- only get success or failure
1164: if (prefHelper.hasPrefWithAspectType(newtask,
1165: AspectType.QUANTITY))
1166: prefHelper.removePrefWithAspectType(newtask,
1167: AspectType.QUANTITY);
1168:
1169: attachRoute(parentTask, newtask);
1170:
1171: return newtask;
1172: }
1173:
1174: /**
1175: * <pre>
1176: * Attaches two preps to task : SEAROUTE and SEAROUTE_DISTANCE
1177: *
1178: * SEAROUTE will appear in the TPFDD Viewer as the path of the ship, if the
1179: * item goes by ship. SEAROUTE_DISTANCE is used by the TRANSCOM vishnu scheduler
1180: * to make the sea-vs-air decision. (Specifically, it decides if at a reasonable
1181: * ship speed (~15 knots), the task could be completed in the time allowed by ship,
1182: * and if so, decides to send the item by ship.)
1183: * </pre>
1184: * @param parentTask - used to calculate the route (using the from-to pair)
1185: * @param subtask - task to attach the preps to
1186: */
1187: protected void attachRoute(Task parentTask, Task subtask) {
1188: TransportationRoute route = portLocator.getRoute(parentTask);
1189:
1190: glmPrepHelper.addPrepToTask(subtask, glmPrepHelper
1191: .makePrepositionalPhrase(ldmf, GLMTransConst.SEAROUTE,
1192: route));
1193: Distance distance = route.getLength();
1194: glmPrepHelper.addPrepToTask(subtask, glmPrepHelper
1195: .makePrepositionalPhrase(ldmf,
1196: GLMTransConst.SEAROUTE_DISTANCE, distance));
1197: }
1198:
1199: /**
1200: * <pre>
1201: * Function that breaks up a AssetGroup or AggregateAsset into smaller pieces.
1202: * Return values should only by Assets and small AggregateAssets (derived classes
1203: * must determine how big is OK. By default all Aggregate are broken up).
1204: *
1205: * If myExpandAggregates is true, expands both aggregate assets and asset groups.
1206: * If it's false, only expands asset groups.
1207: *
1208: * </pre>
1209: * @param asset
1210: * @return A vector of assets that this asset has been broken into.
1211: **/
1212: public Vector expandAsset(Task task, Asset asset) {
1213: if (myExpandAggregates) {
1214: if (isDebugEnabled())
1215: debug(getName()
1216: + ".expandAsset - expanding aggregate asset "
1217: + asset);
1218:
1219: PlanningFactory ldmf = (PlanningFactory) getDomainService()
1220: .getFactory("planning");
1221: Vector items = glmAssetHelper.ExpandAsset(ldmf, asset);
1222: if (isDebugEnabled())
1223: debug(getName() + ".expandAsset - aggregate asset had "
1224: + items.size() + " items.");
1225: return items;
1226: } else if (asset instanceof AssetGroup) {
1227: if (isDebugEnabled())
1228: debug(getName()
1229: + ".expandAsset - expanding asset group "
1230: + asset);
1231: return glmAssetHelper.expandAssetGroup((AssetGroup) asset);
1232: } else {
1233: if (isDebugEnabled())
1234: debug(getName() + ".expandAsset - not expanding "
1235: + asset);
1236: }
1237:
1238: Vector vector = new Vector();
1239: vector.add(asset);
1240:
1241: return vector;
1242: }
1243:
1244: /** Buffering runnable wants to restart later */
1245: public void startAgainIn(long millis) {
1246: if (isInfoEnabled())
1247: info(getName() + " asking to be restarted in " + millis);
1248:
1249: if (currentAlarm != null)
1250: currentAlarm.cancel();
1251:
1252: alarmService
1253: .addAlarm(currentAlarm = new BufferingAlarm(millis));
1254: }
1255:
1256: public void setThreadService(ThreadService ts) {
1257: threadService = ts;
1258: }
1259:
1260: /** Alarm for when buffering runnable wants to restart later */
1261: class BufferingAlarm implements Alarm {
1262: long clockExpireTime;
1263: boolean expired = false;
1264:
1265: public BufferingAlarm(long ticksToWait) {
1266: clockExpireTime = alarmService.currentTimeMillis()
1267: + ticksToWait; // based on wall clock time
1268: }
1269:
1270: /** @return absolute time (in milliseconds) that the Alarm should
1271: * go off.
1272: * This value must be implemented as a fixed value.
1273: **/
1274: public long getExpirationTime() {
1275: return clockExpireTime;
1276: }
1277:
1278: /**
1279: * Called by the cluster clock when clock-time >= getExpirationTime().
1280: * The system will attempt to Expire the Alarm as soon as possible on
1281: * or after the ExpirationTime, but cannot guarantee any specific
1282: * maximum latency.
1283: * NOTE: this will be called in the thread of the cluster clock.
1284: * Implementations should make certain that this code does not block
1285: * for a significant length of time.
1286: * If the alarm has been canceled, this should be a no-op.
1287: **/
1288: public synchronized void expire() {
1289: if (!expired) {
1290: synchronized (alarmMutex) { // getSubtasks will check to see if one exists, so must synchronize
1291: currentAlarm = null;
1292: }
1293:
1294: expired = true;
1295:
1296: String name = getName() + "_restartThread";
1297:
1298: if (isInfoEnabled())
1299: info(getName() + " re-examining unprocessed tasks.");
1300:
1301: schedulable = threadService.getThread(this ,
1302: new Runnable() {
1303: public void run() {
1304: // review the known task list
1305: try {
1306: blackboard.openTransaction();
1307: reviewLevel2();
1308: processTasks(new ArrayList(
1309: myInputTaskCallback
1310: .getSubscription()
1311: .getCollection()));
1312: } catch (Throwable t) {
1313: //System.err.println("Error: Uncaught exception in "+this+": "+t);
1314: if (logger.isErrorEnabled()) {
1315: logger
1316: .error("Error: Uncaught exception in "
1317: + this
1318: + ": "
1319: + t);
1320: t.printStackTrace();
1321: }
1322: } finally {
1323: blackboard.closeTransaction();
1324: }
1325: }
1326: }, name);
1327: schedulable.start();
1328: }
1329: }
1330:
1331: /** @return true IFF the alarm has rung (expired) or was canceled. **/
1332: public boolean hasExpired() {
1333: return expired;
1334: }
1335:
1336: /**
1337: * Can be called by a client to cancel the alarm. May or may not remove
1338: * the alarm from the queue, but should prevent expire from doing anything.
1339: * @return false IF the the alarm has already expired or was already canceled.
1340: **/
1341: public synchronized boolean cancel() {
1342: boolean was = expired;
1343: expired = true;
1344: return was;
1345: }
1346:
1347: public String toString() {
1348: return "<BufferingAlarm " + clockExpireTime
1349: + (expired ? "(Expired) " : " ") + "for "
1350: + GLMTransOneToManyExpanderPlugin.this .toString()
1351: + ">";
1352: }
1353: }
1354:
1355: public void reviewLevel2() {
1356: // review the known task list
1357: replanLevel2(blackboard.query(new UnaryPredicate() {
1358: public boolean execute(Object obj) {
1359: if (obj instanceof Task) {
1360: Task task = (Task) obj;
1361: if (interestingTask(task)) {
1362: // if just by looking at the dates it ought to be level 6
1363: if (getMode(task) == LEVEL_6_MODE) {
1364: if (task.getPlanElement() != null
1365: && (task.getPlanElement() instanceof Expansion)) {
1366: Task subtask = (Task) ((Expansion) task
1367: .getPlanElement())
1368: .getWorkflow().getTasks()
1369: .nextElement();
1370: // but it's got low fi children
1371: return (prepHelper.hasPrepNamed(
1372: subtask,
1373: GLMTransConst.LOW_FIDELITY));
1374: } else
1375: return false;
1376: } else
1377: return false;
1378: } else
1379: return false;
1380: } else
1381: return false;
1382: }
1383: }));
1384: }
1385:
1386: /**
1387: * Remove the expansions of tasks that were previously expanded
1388: * into level-2 subtasks. By removing the expansion and publish
1389: * changing the task, the task will appear as a "new" task to the
1390: * plugin again in the next execute cycle. <p>
1391: *
1392: * If the task somehow got it's plan element removed by the time this
1393: * is called, just skip it. Somehow a logic provider seems to be able
1394: * to remove the task's expansion during my transaction. <p>
1395: *
1396: * @param level2Tasks tasks that were previously expanded into level 2
1397: * that we want to replan.
1398: */
1399: protected void replanLevel2(Collection level2Tasks) {
1400: for (Iterator iter = level2Tasks.iterator(); iter.hasNext();) {
1401: NewTask level2 = (NewTask) iter.next();
1402:
1403: PlanElement pe = level2.getPlanElement();
1404: if (pe == null) {
1405: if (isInfoEnabled())
1406: info("Somehow between calling the predicate and trying\n"
1407: + " to remove task "
1408: + level2.getUID()
1409: + "'s plan element\n"
1410: + " the plan element got removed by another Logic Provider.");
1411: continue; // nothing to do
1412: }
1413:
1414: publishRemove(pe);
1415: publishChange(level2);
1416:
1417: // replan the task
1418:
1419: if (isInfoEnabled())
1420: info(getName() + ".reviewLevel2 - replanning task "
1421: + level2.getUID() + " as a Level 6 task.");
1422: }
1423: }
1424:
1425: /**
1426: * <pre>
1427: * NOTE : This is called magically by reflection from BindingUtility.setServices
1428: * setServices looks for any method in a component that starts with "set" and
1429: * tries to find a service X.
1430: *
1431: * More specifically, it's looking for a method signature like:
1432: * setXService (XService s)
1433: *
1434: * </pre>
1435: * @see org.cougaar.core.component.BindingUtility#setServices
1436: */
1437: public void setDomainService(DomainService ds) {
1438: theDomainService = ds;
1439: }
1440:
1441: public final DomainService getDomainService() {
1442: return theDomainService;
1443: }
1444:
1445: //Domain service (factory service piece of old LDM)
1446: private DomainService theDomainService = null;
1447:
1448: /** implemented for BlackboardPlugin interface -- need public access! */
1449: public BlackboardService getBlackboard() {
1450: return blackboard;
1451: }
1452:
1453: // Utility functions ----------------------------------------------------
1454:
1455: private void reportError(String err) {
1456: error(getName() + " : " + err);
1457: }
1458:
1459: protected boolean myExpandAggregates;
1460: protected GLMPrepPhrase glmPrepHelper;
1461: protected GLMPreference glmPrefHelper;
1462: protected AssetUtil glmAssetHelper;
1463: protected PortLocatorImpl portLocator;
1464: protected Alarm currentAlarm;
1465: protected Schedulable schedulable;
1466: protected Object alarmMutex = new Object();
1467: }
|