001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.logistics.plugin.inventory;
028:
029: import org.cougaar.glm.ldm.asset.Inventory;
030: import org.cougaar.glm.ldm.plan.ObjectScheduleElement;
031: import org.cougaar.planning.ldm.plan.*;
032: import org.cougaar.planning.ldm.plan.NewWorkflow;
033: import org.cougaar.planning.plugin.util.PluginHelper;
034: import org.cougaar.logistics.plugin.utils.ScheduleUtils;
035:
036: import java.util.ArrayList;
037: import java.util.Collection;
038: import java.util.Enumeration;
039: import java.util.Iterator;
040: import java.util.HashMap;
041:
042: /** The DiffBasedComparator compares both the set of old refills and new
043: * refills. The goal of this module is to reduce the number of changes
044: * replans make on the blackboard. It accomplishes this by finding published
045: * tasks that are identical to just created tasks. The code will also try
046: * and modify an existing task that is similar to the the newly created task
047: * as opposed to to rescinding the published task and publishing the new task.
048: * Of course, if no published task can be found during the appropriate time span
049: * of the new task, the new task is published. Any unaccounted for published
050: * tasks will be rescinded.
051: *
052: * Called by the Refill Generator with the new refills and old refills.
053: *
054: **/
055:
056: public class DiffBasedComparator extends InventoryModule implements
057: ComparatorModule {
058:
059: /** Need to pass in the IM Plugin for now to get services
060: * and util classes.
061: **/
062: public DiffBasedComparator(InventoryManager imPlugin) {
063: super (imPlugin);
064: if (logger.isDebugEnabled()) {
065: logger.debug("DiffBasedComparator LOADED!!!!!");
066: }
067: }
068:
069: /** Compares the old and new Refill tasks.
070: * The previously published refills are bucketized which means they are
071: * flagged as belonging in a certain bucket. Each new task is examined to
072: * determine the bucket in which it would belong. If a previously published
073: * task is found to occupy the same bucket as a new task, those tasks are
074: * then compared. If the tasks are identical, no blackboard action is taken.
075: * Otherwise, the published task is changed to take on the characteristics
076: * of the new task and the new task is discarded. If no refill occupies the
077: * same bucket as a new refill task, that task is published. Any unaccounted
078: * for published tasks are rescinded.
079: * @param newRefills The collection of newly generated Refills from the
080: * RefillGeneratorModule
081: * @param oldRefills The previously generated Refill Tasks
082: * @param inv The Inventory the Refills are refilling.
083: **/
084: public void compareRefills(ArrayList newRefills,
085: ArrayList oldRefills, Inventory inv) {
086:
087: //Process all new Refill Tasks
088: LogisticsInventoryPG thePG = (LogisticsInventoryPG) inv
089: .searchForPropertyGroup(LogisticsInventoryPG.class);
090: if (logger.isDebugEnabled()) {
091: logger.debug("DiffSupply handling "
092: + thePG.getResource().getTypeIdentificationPG()
093: .getTypeIdentification());
094: }
095:
096: // Create bucket map from oldRefills for easy access during the compare
097: HashMap publishedTaskMap = new HashMap();
098: Task oldRefill = null;
099: Iterator oldIter = oldRefills.iterator();
100: while (oldIter.hasNext()) {
101: oldRefill = (Task) oldIter.next();
102: if (oldRefill != null) {
103: int bucket = thePG.convertTimeToBucket(getTaskUtils()
104: .getEndTime(oldRefill), false);
105: publishedTaskMap.put(new Integer(bucket), oldRefill);
106: }
107: }
108:
109: // Compare new refills to old (published) refills
110: Task newRefill = null, publishedRefill = null, updatedTask = null;
111: Iterator newIter = newRefills.iterator();
112: while (newIter.hasNext()) {
113: newRefill = (Task) newIter.next();
114: int bucket = thePG.convertTimeToBucket(getTaskUtils()
115: .getEndTime(newRefill), false);
116: publishedRefill = (Task) publishedTaskMap.get(new Integer(
117: bucket));
118: if (publishedRefill == null) {
119: // No published refill for the bucket, just publish the new refill
120: if (logger.isDebugEnabled()) {
121: logger.debug("DiffSupply "
122: + getTimeUtils().dateString(
123: thePG.convertBucketToTime(bucket))
124: + " - "
125: + "no refills on this day, add new task");
126: }
127: if (inventoryPlugin.publishRefillTask(newRefill, inv)) {
128: thePG.addRefillRequisition(newRefill);
129: } else {
130: if (logger.isDebugEnabled()) {
131: logger
132: .debug("publishRefillTask returned false - not adding Refill task to the BG"
133: + newRefill.getUID());
134: }
135: }
136: } else {
137: // found a published refill for the bucket
138: oldRefills.remove(publishedRefill);
139: updatedTask = getTaskUtils().changeTask(
140: publishedRefill, newRefill);
141: if (updatedTask == null) {// null indicates tasks are identical
142: if (logger.isDebugEnabled()) {
143: logger
144: .debug("DiffSupply "
145: + getTimeUtils()
146: .dateString(
147: thePG
148: .convertBucketToTime(bucket))
149: + " - "
150: + "new task identical to published task.");
151: }
152: thePG.addRefillRequisition(publishedRefill);
153: } else {// changeTask() returned the updated task
154: if (logger.isDebugEnabled()) {
155: logger
156: .debug("DiffSupply "
157: + getTimeUtils()
158: .dateString(
159: thePG
160: .convertBucketToTime(bucket))
161: + " - "
162: + "changed published task to match new task");
163: }
164: inventoryPlugin.publishChange(updatedTask);
165: thePG.addRefillRequisition(updatedTask);
166: }
167: }
168: }
169:
170: //Rescind all old Refill Tasks that have not been accounted for
171: oldIter = oldRefills.iterator();
172: while (oldIter.hasNext()) {
173: oldRefill = (Task) oldIter.next();
174: // check for a null in the refills list!
175: if (oldRefill != null) {
176: // clean out the reference in the maintain inventory workflow
177: if (logger.isDebugEnabled()) {
178: logger
179: .debug("DiffSupply Remove unwanted published task from previous plan "
180: + getTaskUtils()
181: .taskDesc(oldRefill));
182: }
183: //((NewWorkflow)oldRefill.getWorkflow()).removeTask(oldRefill);
184: //inventoryPlugin.publishRemove(oldRefill);
185: inventoryPlugin.removeSubTask(oldRefill);
186: }
187: }
188: }
189:
190: /** Compares the old and new Refill Projection tasks.
191: * A schedule is created from the previously published projections. New tasks
192: * are compared to the schedule in order to identify overlapping tasks. In
193: * cases where overlapping tasks are found, the published task is changed to
194: * convey the information of the new task. If the tasks are identical no changes
195: * are made to the blackboard. New tasks which have no overlap with existing
196: * tasks are published and published refills which have not been accounted for
197: * are rescinded.
198: * @param newRefillProjs The collection of newly generated Refill Projections
199: * from the RefillProjectionsGenerator Module
200: * @param oldRefillProjs The previously generated Refill Projection Tasks
201: * @param inv The Inventory the Refills Projections are refilling.
202: **/
203: public void compareRefillProjections(ArrayList newRefillProjs,
204: ArrayList oldRefillProjs, Inventory inv) {
205:
206: LogisticsInventoryPG thePG = (LogisticsInventoryPG) inv
207: .searchForPropertyGroup(LogisticsInventoryPG.class);
208:
209: if (logger.isDebugEnabled()) {
210: logger.debug("DiffProj handling "
211: + thePG.getResource().getTypeIdentificationPG()
212: .getTypeIdentification());
213: }
214: // Check for an empty schedule
215: if ((newRefillProjs == null) || newRefillProjs.isEmpty()) {
216: // Rescind all tasks as there is no longer any demand.
217: if (logger.isDebugEnabled()) {
218: logger.debug("DiffProj, New Task List empty: "
219: + newRefillProjs);
220: }
221: Iterator list = oldRefillProjs.iterator();
222: while (list.hasNext()) {
223: Task oldRefill = (Task) list.next();
224: if (logger.isDebugEnabled()) {
225: logger.debug("DiffProj \n"
226: + getTaskUtils().taskDesc(oldRefill));
227: }
228: if (oldRefill != null) {
229: // clean out the reference in the maintain inventory workflow
230: //((NewWorkflow)oldRefill.getWorkflow()).removeTask(oldRefill);
231: //inventoryPlugin.publishRemove(oldRefill);
232: inventoryPlugin.removeSubTask(oldRefill);
233: }
234: }
235: return;
236: }
237:
238: Schedule publishedSchedule = getTaskUtils().newObjectSchedule(
239: oldRefillProjs);
240: Schedule newTaskSchedule = getTaskUtils().newObjectSchedule(
241: newRefillProjs);
242:
243: // Compare new tasks to previously scheduled tasks, if a published task is found that
244: // spans the new task's start time then adjust the published task (if needed) and publish
245: // the change. If no task is found than add new_task to list of tasks to be published.
246: long start;
247: Task new_task = null, published_task = null;
248: ObjectScheduleElement ose = null;
249: Collection c = null;
250: while (!newTaskSchedule.isEmpty()) {
251: start = newTaskSchedule.getStartTime();
252: ose = (ObjectScheduleElement) ScheduleUtils
253: .getElementWithTime(newTaskSchedule, start);
254: if (ose != null) {
255: new_task = (Task) ose.getObject();
256: ((NewSchedule) newTaskSchedule)
257: .removeScheduleElement(ose);
258: } else {
259: logger.error("DiffProj, Bad Schedule: "
260: + newTaskSchedule);
261: return;
262: }
263: // Get overlapping schedule elements from start to end of new task
264: c = publishedSchedule.getScheduleElementsWithTime(start);
265: if (!c.isEmpty()) {
266: // change the task to look like new task
267: ose = (ObjectScheduleElement) c.iterator().next();
268: published_task = (Task) ose.getObject();
269: Task saveTask = published_task;
270: ((NewSchedule) publishedSchedule)
271: .removeScheduleElement(ose);
272:
273: if (logger.isDebugEnabled()) {
274: logger.debug(" Comparing published task "
275: + getTaskUtils().taskDesc(published_task)
276: + " with \n"
277: + getTaskUtils().taskDesc(new_task));
278: }
279: // changeTask returns the changed published task if the 2 tasks are different and
280: // null if the tasks are identical.
281: published_task = getTaskUtils().changeTask(
282: published_task, new_task);
283: if (published_task != null) {
284: if (logger.isDebugEnabled()) {
285: logger.debug("DiffProj published task changed "
286: + getTaskUtils().taskDesc(
287: published_task));
288: }
289: inventoryPlugin.publishChange(published_task);
290: thePG.addRefillProjection(published_task);
291: } else { // published and new task are the same, so add the published task back into the BG
292: thePG.addRefillProjection(saveTask);
293: if (logger.isDebugEnabled()) {
294: logger
295: .debug("DiffProj published task identical to new task"
296: + getTaskUtils().taskDesc(
297: saveTask));
298: }
299: }
300: } else {
301: // no task exists that covers this timespan, publish it and
302: // apply the Task to the LogisticsInventoryBG
303: if (logger.isDebugEnabled()) {
304: logger
305: .debug("No task exists that covers this timespan, publish task "
306: + getTaskUtils().taskDesc(new_task));
307: }
308: if (inventoryPlugin.publishRefillTask(new_task, inv)) {
309: thePG.addRefillProjection(new_task);
310: } else {
311: if (logger.isDebugEnabled()) {
312: logger
313: .debug("publishRefillTask returned false - not adding Refill task to the BG"
314: + new_task.getUID());
315: }
316: }
317: }
318: }
319: // Rescind any tasks that were not accounted for
320: Enumeration e = publishedSchedule.getAllScheduleElements();
321: while (e.hasMoreElements()) {
322: Task task = (Task) ((ObjectScheduleElement) e.nextElement())
323: .getObject();
324: if (logger.isDebugEnabled()) {
325: logger
326: .debug("DiffProj Remove unwanted published task from previous plan "
327: + getTaskUtils().taskDesc(task));
328: }
329: //((NewWorkflow)task.getWorkflow()).removeTask(task);
330: //inventoryPlugin.publishRemove(task);
331: if (logger.isDebugEnabled()) {
332: logger
333: .debug("About to call pluginhelper.removeSubTask... Task is: "
334: + task.getVerb()
335: + " "
336: + task.getUID()
337: + " Parent Task is: "
338: + task.getParentTaskUID()
339: + " "
340: + task.getWorkflow().getParentTask()
341: .getVerb()
342: + " Parent PE is: "
343: + task.getWorkflow().getParentTask()
344: .getPlanElement());
345: }
346: inventoryPlugin.removeSubTask(task);
347: }
348: }
349: }
|