001: /*
002: * <copyright>
003: *
004: * Copyright 1999-2004 Honeywell Inc
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.glm.packer;
028:
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Comparator;
032: import java.util.Enumeration;
033: import java.util.Iterator;
034:
035: import org.cougaar.core.blackboard.IncrementalSubscription;
036: import org.cougaar.planning.ldm.plan.AllocationResult;
037: import org.cougaar.planning.ldm.plan.AllocationResultDistributor;
038: import org.cougaar.planning.ldm.plan.AspectType;
039: import org.cougaar.planning.ldm.plan.AspectValue;
040: import org.cougaar.planning.ldm.plan.PlanElement;
041: import org.cougaar.planning.ldm.plan.Task;
042: import org.cougaar.planning.plugin.util.PluginHelper;
043: import org.cougaar.util.Sortings;
044: import org.cougaar.util.UnaryPredicate;
045:
046: /**
047: * Packer - handles packing supply requests
048: *
049: */
050: public abstract class Packer extends GenericPlugin {
051: private int ADD_TASKS = 0;
052: private int MODIFIED_TASKS = 0;
053: private int REMOVE_TASKS = 0;
054: private double ADD_TONS = 0;
055: private double REMOVE_TONS = 0;
056:
057: /**
058: * Packer - constructor
059: */
060: public Packer() {
061: super ();
062: }
063:
064: /**
065: * getSortFunction - returns comparator to be used in sorting the tasks to be
066: * packed. Default implementation sorts on end time.
067: *
068: * @return Comparator
069: */
070: public Comparator getSortFunction() {
071: return new SortByEndTime();
072: }
073:
074: /**
075: * getAllocationResultDistributor - returns the AllocationResultDistributor be
076: * used in distributing allocation result for the transport task among the initial
077: * supply tasks. Defaults to
078: * ProportionalDistributor.DEFAULT_PROPORTIONAL_DISTRIBUTOR;
079: *
080: * @return AllocationResultDistributor
081: */
082: public AllocationResultDistributor getAllocationResultDistributor() {
083: return ProportionalDistributor.DEFAULT_PROPORTIONAL_DISTRIBUTOR;
084: }
085:
086: /**
087: * getPreferenceAggregator - returns PreferenceAggregator for setting the
088: * start/end times on the transport tasks. Defaults to DefaultPreferenceAggregator.
089: *
090: * @return PreferenceAggregator
091: */
092: public PreferenceAggregator getPreferenceAggregator() {
093: return new DefaultPreferenceAggregator();
094: }
095:
096: /**
097: * getAggregationClosure - return AggregationClosure to be used for creating
098: * transport tasks
099: */
100: public abstract AggregationClosure getAggregationClosure(
101: ArrayList tasks);
102:
103: public int getTaskQuantityUnit() {
104: return Sizer.TONS;
105: }
106:
107: /**
108: * processNewTasks - handle new ammo supply tasks
109: * Called within GenericPlugin.execute.
110: *
111: * @param newTasks Enumeration of the new tasks
112: */
113: public void processNewTasks(Enumeration newTasks) {
114: ArrayList tasks = new ArrayList();
115:
116: double tonsReceived = 0;
117:
118: while (newTasks.hasMoreElements()) {
119: Task task = (Task) newTasks.nextElement();
120: if (task.getPlanElement() != null) {
121: getLoggingService()
122: .warn(
123: "Packer: Unable to pack - "
124: + task.getUID()
125: + " - task already has a PlanElement - "
126: + task.getPlanElement()
127: + ".\n"
128: + "Is the UniversalAllocator also handling Supply tasks in this node?");
129: } else {
130: if (getLoggingService().isInfoEnabled()) {
131: getLoggingService().info(
132: "Packer: Got a new task - " + task.getUID()
133: + " from " + task.getSource());
134: }
135: ADD_TASKS++;
136:
137: double taskWeight = Sizer.getTaskMass(task,
138: getTaskQuantityUnit()).getShortTons();
139: ADD_TONS += taskWeight;
140: tonsReceived += taskWeight;
141: tasks.add(task);
142: }
143: }
144:
145: if (tasks.size() == 0) {
146: return;
147: }
148:
149: if (getLoggingService().isDebugEnabled()) {
150: getLoggingService()
151: .debug(
152: "Packer - number of added SUPPLY tasks: "
153: + ADD_TASKS
154: + ", aggregated quantity from added SUPPLY tasks: "
155: + ADD_TONS + " tons.");
156: }
157:
158: double tonsPacked = doPacking(tasks, getSortFunction(),
159: getPreferenceAggregator(),
160: getAllocationResultDistributor());
161:
162: if ((tonsPacked > tonsReceived + 0.1)
163: || (tonsPacked < tonsReceived - 0.1)) {
164: if (getLoggingService().isErrorEnabled()) {
165: getLoggingService().warn(
166: "Packer - received " + tonsReceived
167: + " tons but packed " + tonsPacked
168: + " tons, (total received " + ADD_TONS
169: + " vs total packed "
170: + Filler.TRANSPORT_TONS
171: + ") for tasks : ");
172: for (Iterator iter = tasks.iterator(); iter.hasNext();)
173: getLoggingService().warn(
174: "\t" + ((Task) iter.next()).getUID());
175: }
176: }
177: }
178:
179: /**
180: * processChangedTasks - handle changed supply tasks
181: * Called within GenericPlugin.execute.
182: * **** Tasks are currently ignored ****
183: *
184: * @param changedTasks Enumeration of changed ammo supply tasks. Ignored.
185: */
186: public void processChangedTasks(Enumeration changedTasks) {
187: while (changedTasks.hasMoreElements()) {
188: Task task = (Task) changedTasks.nextElement();
189:
190: if (getLoggingService().isDebugEnabled()) {
191: getLoggingService().debug(
192: "Packer - ignoring changed task - "
193: + task.getUID() + " from "
194: + task.getSource());
195: }
196:
197: }
198: }
199:
200: /**
201: * processRemovedTasks - handle removed supply tasks
202: * Called within GenericPlugin.execute.
203: * **** Tasks are currently ignored ****
204: *
205: * @param removedTasks Enumeration of removed ammo supply tasks. Ignored.
206: */
207: public void processRemovedTasks(Enumeration removedTasks) {
208: boolean anyRemoved = false;
209:
210: while (removedTasks.hasMoreElements()) {
211: anyRemoved = true;
212: Task task = (Task) removedTasks.nextElement();
213:
214: if (getLoggingService().isInfoEnabled()) {
215: getLoggingService().info(
216: "Packer: Got a removed task - " + task.getUID()
217: + " from " + task.getSource());
218: }
219:
220: REMOVE_TASKS++;
221: REMOVE_TONS += task.getPreferredValue(AspectType.QUANTITY);
222:
223: if (getLoggingService().isInfoEnabled()) {
224: getLoggingService()
225: .info(
226: "Packer - number of removed SUPPLY tasks: "
227: + REMOVE_TASKS
228: + ", aggregated quantity from removed SUPPLY tasks: "
229: + REMOVE_TONS + " tons.");
230: }
231: }
232:
233: if (anyRemoved) {
234: Collection unplannedInternal = getBlackboardService()
235: .query(new UnaryPredicate() {
236: public boolean execute(Object obj) {
237: if (obj instanceof Task) {
238: Task task = (Task) obj;
239: return ((task
240: .getPrepositionalPhrase(GenericPlugin.INTERNAL) != null) && task
241: .getPlanElement() == null);
242: }
243: return false;
244: }
245: });
246:
247: handleUnplanned(unplannedInternal);
248: }
249: }
250:
251: protected void handleUnplanned(Collection unplanned) {
252: if (getLoggingService().isInfoEnabled())
253: getLoggingService().info(
254: "Packer: found " + unplanned.size()
255: + " tasks -- replanning them!");
256:
257: for (Iterator iter = unplanned.iterator(); iter.hasNext();) {
258: Task task = (Task) iter.next();
259:
260: ArrayList copy = new ArrayList();
261: copy.add(task);
262: AggregationClosure ac = getAggregationClosure(copy);
263:
264: Filler fil = new Filler(null, this , ac,
265: getAllocationResultDistributor(),
266: getPreferenceAggregator());
267:
268: fil.handleUnplanned(task);
269: }
270: }
271:
272: /**
273: * doPacking - packs specified set of supply tasks.
274: * Assumes that it's called within an open/close transaction.
275: *
276: * @param tasks ArrayList with the tasks which should be packed
277: * @param sortfun BinaryPredicate to be used in sorting the tasks
278: * @param prefagg PreferenceAggregator for setting the start/end times on the
279: * transport tasks.
280: * @param ard AllocationResultDistributor to be used in distributing allocation results
281: * for the transport task amount the initial supply tasks. *
282: */
283: protected double doPacking(ArrayList tasks, Comparator sortfun,
284: PreferenceAggregator prefagg,
285: AllocationResultDistributor ard) {
286:
287: // Divide into 'pack together' groups
288: Collection packGroups = groupByAggregationClosure(tasks);
289:
290: double totalPacked = 0;
291:
292: for (Iterator iterator = packGroups.iterator(); iterator
293: .hasNext();) {
294: ArrayList packList = (ArrayList) iterator.next();
295: // sort them, if appropriate
296: if (sortfun != null) {
297: packList = (ArrayList) Sortings.sort(packList, sortfun);
298: }
299:
300: if (getLoggingService().isDebugEnabled()) {
301: getLoggingService()
302: .debug(
303: "Packer: about to build the sizer in doPacking.");
304: }
305:
306: AggregationClosure ac = getAggregationClosure(packList);
307:
308: // now we set the double wheel going...
309: Sizer sz = new Sizer(packList, this , getTaskQuantityUnit());
310:
311: if (getLoggingService().isDebugEnabled()) {
312: getLoggingService()
313: .debug(
314: "Packer: about to build the filler in doPacking.");
315: }
316:
317: Filler fil = new Filler(sz, this , ac, ard, prefagg);
318:
319: if (getLoggingService().isDebugEnabled()) {
320: getLoggingService()
321: .debug(
322: "Packer: about to run the wheelz in doPacking.");
323: }
324:
325: totalPacked += fil.execute();
326: }
327:
328: return totalPacked;
329: }
330:
331: protected void setupSubscriptions() {
332: super .setupSubscriptions();
333: ProportionalDistributor.DEFAULT_PROPORTIONAL_DISTRIBUTOR
334: .setLoggingService(getLoggingService());
335: }
336:
337: protected void updateAllocationResult(
338: IncrementalSubscription planElements) {
339: // Make sure that quantity preferences get returned on the allocation
340: // results. Transport thread may not have filled them in.
341: Enumeration changedPEs = planElements.getChangedList();
342: while (changedPEs.hasMoreElements()) {
343: PlanElement pe = (PlanElement) changedPEs.nextElement();
344:
345: // Only update the plan element if this is a change to the reported
346: // result.
347: if (PluginHelper.checkChangeReports(planElements
348: .getChangeReports(pe),
349: PlanElement.ReportedResultChangeReport.class)
350: && PluginHelper.updatePlanElement(pe)) {
351: boolean needToCorrectQuantity = false;
352:
353: AllocationResult estimatedAR = pe.getEstimatedResult();
354: double prefValue = pe.getTask().getPreference(
355: AspectType.QUANTITY).getScoringFunction()
356: .getBest().getAspectValue().getValue();
357:
358: AspectValue[] aspectValues = estimatedAR
359: .getAspectValueResults();
360:
361: // Possibly need to add quantity to list of aspects if it's not there in the first place.
362: // Couldn't see that this was in fact necessary so leaving it out for the moment
363: // Gordon Vidaver 08/23/02
364:
365: boolean foundQuantity = false;
366: for (int i = 0; i < aspectValues.length; i++) {
367: if (aspectValues[i].getAspectType() == AspectType.QUANTITY) {
368: if (aspectValues[i].getValue() != prefValue) {
369: // set the quantity to be the preference quantity
370: aspectValues[i] = aspectValues[i]
371: .dupAspectValue(prefValue);
372: needToCorrectQuantity = true;
373: }
374: foundQuantity = true;
375: break;
376: }
377: }
378:
379: if (!foundQuantity) {
380: AspectValue[] copy = new AspectValue[aspectValues.length + 1];
381: System.arraycopy(aspectValues, 0, copy, 0,
382: aspectValues.length);
383: copy[aspectValues.length] = AspectValue
384: .newAspectValue(AspectType.QUANTITY,
385: prefValue);
386: aspectValues = copy;
387: }
388:
389: if (needToCorrectQuantity) {
390: if (getLoggingService().isDebugEnabled()) {
391: getLoggingService().debug(
392: "Packer.updateAllocationResult - fixing quantity on estimated AR of pe "
393: + pe.getUID());
394: }
395:
396: AllocationResult correctedAR = new AllocationResult(
397: estimatedAR.getConfidenceRating(),
398: estimatedAR.isSuccess(), aspectValues);
399:
400: pe.setEstimatedResult(correctedAR);
401: }
402:
403: publishChange(pe);
404: }
405: }
406: }
407:
408: /**
409: * SortByEndTime - sorts tasks by end date, earliest first
410: */
411: private class SortByEndTime implements Comparator {
412:
413: /*
414: * compare - compares end date of the 2 tasks.
415: * Compares its two arguments for order. Returns a negative integer, zero, or a
416: * positive integer as the first argument is less than, equal
417: * to, or greater than the second.
418: */
419: public int compare(Object first, Object second) {
420: Task firstTask = null;
421: Task secondTask = null;
422:
423: if (first instanceof Task) {
424: firstTask = (Task) first;
425: }
426:
427: if (second instanceof Task) {
428: secondTask = (Task) second;
429: }
430:
431: if ((firstTask == null) && (secondTask == null)) {
432: return 0;
433: } else if (firstTask == null) {
434: return -1;
435: } else if (secondTask == null) {
436: return 1;
437: } else {
438: return (firstTask
439: .getPreferredValue(AspectType.END_TIME) > secondTask
440: .getPreferredValue(AspectType.END_TIME)) ? 1
441: : -1;
442: }
443: }
444:
445: /**
446: * Indicates whether some other object is "equal to" this Comparator.
447: * This method must obey the general contract of Object.equals(Object).
448: * Additionally, this method can return true only if the specified Object is
449: * also a comparator and it imposes the same ordering as this comparator. Thus,
450: * comp1.equals(comp2) implies that sgn(comp1.compare(o1,
451: * o2))==sgn(comp2.compare(o1, o2)) for every object reference o1 and o2.
452: */
453: public boolean equals(Object o) {
454: return (o.getClass() == SortByEndTime.class);
455: }
456: }
457:
458: protected abstract Collection groupByAggregationClosure(
459: Collection tasks);
460: }
|