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.demand;
028:
029: import org.cougaar.glm.ldm.plan.AlpineAspectType;
030: import org.cougaar.glm.ldm.plan.GeolocLocation;
031: import org.cougaar.glm.ldm.plan.ObjectScheduleElement;
032: import org.cougaar.glm.ldm.plan.PlanScheduleElementType;
033: import org.cougaar.logistics.ldm.Constants;
034: import org.cougaar.logistics.plugin.inventory.MaintainedItem;
035: import org.cougaar.logistics.plugin.utils.ScheduleUtils;
036: import org.cougaar.planning.ldm.asset.*;
037: import org.cougaar.planning.ldm.measure.*;
038: import org.cougaar.planning.ldm.plan.*;
039: import org.cougaar.planning.plugin.util.PluginHelper;
040: import org.cougaar.util.TimeSpan;
041:
042: import java.lang.reflect.InvocationTargetException;
043: import java.lang.reflect.Method;
044: import java.util.*;
045:
046: /**
047: * <pre>
048: * The default ProjectionsExpander for the DemandForecastPlugin.
049: *
050: * This class expands a generate projections task into ProjectSupply tasks for all the
051: * resource components (Parts,fuel,ammo) of the supply class needed by the GP's MEI.
052: *
053: *
054: **/
055:
056: public class GenerateProjectionsExpander extends DemandForecastModule
057: implements GenProjExpanderIfc {
058:
059: private String myOrgName = null;
060:
061: public GenerateProjectionsExpander(DemandForecastPlugin dfPlugin) {
062: super (dfPlugin);
063: }
064:
065: /**
066: * Expand the passed in GenerateProjectins task into the requisite ProjectSupply
067: * tasks - one for each resource need of this MEI/Asset determined by the
068: * BG associated with the passed in supplyPGClass.
069: **/
070: public void expandGenerateProjections(Task gpTask,
071: Schedule schedule, Asset consumer, TimeSpan timespan) {
072:
073: // TODO: document, add logger, handle null checks
074:
075: consumer = convertAggregateToAsset(consumer);
076: PropertyGroup pg = consumer.searchForPropertyGroup(dfPlugin
077: .getSupplyClassPG());
078:
079: dfPlugin.updateStartAndEndTimes();
080:
081: PlanElement pe = gpTask.getPlanElement();
082: if ((pe != null) && !(pe instanceof Disposition)) {
083: if (pe instanceof Expansion) {
084: handleExpandedGpTask(gpTask, schedule, consumer, pg,
085: timespan);
086: }
087: } else {
088: if (pe != null) {
089: dfPlugin.publishRemove(pe);
090: }
091: Collection subTasks = new ArrayList(0);
092: if (schedule != null) {
093: //We are trimming the time span when task scheduler is off down to currentTimeMillis start time
094: //to only project from here on out.
095: Schedule trimmedSchedule = ScheduleUtils
096: .trimObjectSchedule(schedule, timespan);
097: subTasks = buildTaskList(pg, getConsumed(pg),
098: trimmedSchedule, gpTask, consumer);
099: }
100: if (!subTasks.isEmpty()) {
101: createAndPublishExpansion(gpTask, subTasks);
102: } else {
103: if (logger.isInfoEnabled()) {
104: logger.info("Creating a disposition for gpTask "
105: + gpTask);
106: }
107: createDisposition(gpTask);
108: }
109: }
110: }
111:
112: protected void handleExpandedGpTask(Task gpTask, Schedule schedule,
113: Asset consumer, PropertyGroup pg, TimeSpan timespan) {
114: Collection consumedItems = getConsumed(pg);
115:
116: ArrayList assetList = new ArrayList(1);
117: for (Iterator iterator = consumedItems.iterator(); iterator
118: .hasNext();) {
119: Asset asset = (Asset) iterator.next();
120: Collection publishedTasks = dfPlugin.projectSupplySet(
121: gpTask, asset);
122: assetList.clear();
123: assetList.add(asset);
124: if (logger.isDebugEnabled()) {
125: logger.debug("Handling consumed item "
126: + dfPlugin.getAssetUtils().getAssetIdentifier(
127: asset));
128: }
129: Collection newTasks = buildTaskList(pg, assetList,
130: schedule, gpTask, consumer);
131: if (publishedTasks.isEmpty() && newTasks.isEmpty()) {
132: continue;
133: }
134: Schedule publishedTasksSched = getTaskUtils()
135: .newObjectSchedule(publishedTasks);
136: Schedule newTasksSched = getTaskUtils().newObjectSchedule(
137: newTasks);
138:
139: Collection diffedTasks = diffProjections(
140: publishedTasksSched, newTasksSched, timespan);
141: addToAndPublishExpansion(gpTask, diffedTasks);
142: }
143: }
144:
145: protected void createDisposition(Task gpTask) {
146: // FIX ME LATER... Dispose of GPs that don't have any subtasks.
147: // Later we won't create the GPS after we get the new db table that
148: // defines which MEIs really are type x consumers.
149: AspectValue avs[] = new AspectValue[1];
150: avs[0] = AspectValue.newAspectValue(AspectType.START_TIME,
151: getTaskUtils().getPreference(gpTask,
152: AspectType.START_TIME));
153: AllocationResult dispAR = getPlanningFactory()
154: .newAllocationResult(Constants.Confidence.OBSERVED,
155: true, avs);
156: Disposition disp = getPlanningFactory().createDisposition(
157: gpTask.getPlan(), gpTask, dispAR);
158: dfPlugin.publishAdd(disp);
159: }
160:
161: protected Collection buildTaskList(PropertyGroup pg,
162: Collection items, Schedule schedule, Task gpTask,
163: Asset consumer) {
164: if (schedule == null || items.isEmpty()) {
165: return Collections.EMPTY_LIST;
166: }
167:
168: List subTasks = new ArrayList(items.size());
169: for (Iterator iterator = items.iterator(); iterator.hasNext();) {
170: Asset consumedItem = (Asset) iterator.next();
171:
172: Schedule rate_schedule = buildRateSchedule(pg,
173: consumedItem, schedule, consumer);
174:
175: subTasks.add(createProjectSupplyTask(gpTask, consumer,
176: consumedItem, rate_schedule));
177: }
178: return subTasks;
179: }
180:
181: protected Schedule buildRateSchedule(PropertyGroup pg,
182: Asset consumedItem, Schedule schedule, Asset consumer) {
183:
184: List rate_schedule_elements = new ArrayList();
185:
186: Enumeration scheduleElements = schedule
187: .getAllScheduleElements();
188: while (scheduleElements.hasMoreElements()) {
189: ObjectScheduleElement ose = (ObjectScheduleElement) scheduleElements
190: .nextElement();
191: Rate rate = getRate(pg, consumedItem, getRateParams(ose));
192: // A null rate can happen at both ends of the schedule where the MEI
193: // is available but there is no matching org act for the time period.
194: // (if the orgact is null then the bg returns a null rate)
195: if (rate == null) {
196: continue;
197: }
198: if (getTaskUtils().getDailyQuantity(rate) <= 0.0) {
199: if (logger.isWarnEnabled()) {
200: logger.warn(getAssetUtils().getAssetIdentifier(
201: consumedItem)
202: + " on "
203: + getAssetUtils().getAssetIdentifier(
204: consumer)
205: + " has a zero rate for period "
206: + getTimeUtils().dateString(
207: ose.getStartTime())
208: + " - "
209: + getTimeUtils().dateString(
210: ose.getEndTime()));
211: }
212: continue;
213: }
214: if (logger.isInfoEnabled()) {
215: logger.info("checking Rate on "
216: + dfPlugin.getAssetUtils().getAssetIdentifier(
217: consumedItem) + " rate "
218: + getDailyQuantity(rate));
219: }
220: ObjectScheduleElement rse = new ObjectScheduleElement(ose
221: .getStartTime(), ose.getEndTime(), rate);
222: rate_schedule_elements.add(rse);
223: }
224:
225: ScheduleImpl rate_schedule = new ScheduleImpl();
226: rate_schedule
227: .setScheduleElementType(PlanScheduleElementType.OBJECT);
228: rate_schedule.setScheduleType(ScheduleType.OTHER);
229: rate_schedule.setScheduleElements(rate_schedule_elements);
230:
231: return rate_schedule;
232: }
233:
234: protected List getRateParams(ObjectScheduleElement ose) {
235: return (List) ose.getObject();
236: }
237:
238: protected Asset convertAggregateToAsset(Asset consumer) {
239: if (consumer instanceof AggregateAsset) {
240: consumer = ((AggregateAsset) consumer).getAsset();
241: }
242: return consumer;
243: }
244:
245: /** Create FOR, TO, MAINTAIN, OFTYPE, and DEMANDRATE prepositional phrases
246: * for use by the subclasses.
247: * @param consumer the consumer the task supports
248: * @return Vector of PrepostionalPhrases
249: **/
250: protected Vector createPrepPhrases(Object consumer,
251: Task parentTask, long end, Schedule rate_schedule) {
252: Vector prepPhrases = new Vector();
253:
254: prepPhrases
255: .addElement(newPrepositionalPhrase(
256: Constants.Preposition.OFTYPE, dfPlugin
257: .getSupplyType()));
258: prepPhrases.addElement(newPrepositionalPhrase(
259: Constants.Preposition.FOR, getOrgName()));
260:
261: if (rate_schedule != null) {
262: prepPhrases.addElement(newPrepositionalPhrase(
263: Constants.Preposition.DEMANDRATE, rate_schedule));
264: }
265:
266: createGeolocPrepPhrases(parentTask, end, prepPhrases);
267:
268: if (consumer != null) {
269: createMaintainingPrepPhrases(consumer, prepPhrases);
270: }
271:
272: return prepPhrases;
273: }
274:
275: protected void createMaintainingPrepPhrases(Object consumer,
276: Vector prepPhrases) {
277: MaintainedItem itemID;
278: if (consumer instanceof Asset) {
279: TypeIdentificationPG tip = ((Asset) consumer)
280: .getTypeIdentificationPG();
281: ItemIdentificationPG iip = ((Asset) consumer)
282: .getItemIdentificationPG();
283: if (iip != null) {
284: itemID = MaintainedItem.findOrMakeMaintainedItem(
285: "Asset", tip.getTypeIdentification(), iip
286: .getItemIdentification(), tip
287: .getNomenclature(), dfPlugin);
288: } else {
289: itemID = MaintainedItem.findOrMakeMaintainedItem(
290: "Asset", tip.getTypeIdentification(), null, tip
291: .getNomenclature(), dfPlugin);
292: }
293: } else {
294: itemID = MaintainedItem.findOrMakeMaintainedItem("Other",
295: consumer.toString(), null, null, dfPlugin);
296: }
297: prepPhrases.addElement(newPrepositionalPhrase(
298: Constants.Preposition.MAINTAINING, itemID));
299: }
300:
301: protected void createGeolocPrepPhrases(Task parentTask, long end,
302: Vector prepPhrases) {
303: GeolocLocation geoloc = getGeolocLocation(parentTask,
304: (end - 1000));
305: if (geoloc != null) {
306: prepPhrases.addElement(newPrepositionalPhrase(
307: Constants.Preposition.TO, geoloc));
308: } else { // Try to use HomeLocation
309: try {
310: geoloc = (GeolocLocation) dfPlugin.getMyOrganization()
311: .getMilitaryOrgPG().getHomeLocation();
312: prepPhrases.addElement(newPrepositionalPhrase(
313: Constants.Preposition.TO, geoloc));
314: } catch (NullPointerException npe) {
315: if (logger.isErrorEnabled()) {
316: logger
317: .error("demandTaskPrepPhrases(), Unable to find Location for Transport");
318: }
319: }
320: }
321: }
322:
323: protected GeolocLocation getGeolocLocation(Task parent_task,
324: long time) {
325: Enumeration geolocs = getAssetUtils().getGeolocLocationAtTime(
326: dfPlugin.getMyOrganization(), time);
327: if (geolocs.hasMoreElements()) {
328: GeolocLocation geoloc = (GeolocLocation) geolocs
329: .nextElement();
330: // GLMDebug.DEBUG("GenerateSupplyDemandExpander", clusterId_, "At "+TimeUtils.dateString(time)+ " the geoloc is "+geoloc);
331: return geoloc;
332: }
333: return null;
334: }
335:
336: /** Utility accessor to get the Org Name from my organization and keep it around **/
337: private String getOrgName() {
338: if (myOrgName == null) {
339: myOrgName = dfPlugin.getMyOrganization()
340: .getItemIdentificationPG().getItemIdentification();
341: }
342: return myOrgName;
343: }
344:
345: protected void createAndPublishExpansion(Task parent,
346: Collection subtasks) {
347: Workflow wf = buildWorkflow(parent, subtasks);
348: Expansion expansion = getPlanningFactory().createExpansion(
349: parent.getPlan(), parent, wf, null);
350: if (logger.isInfoEnabled()) {
351: logger
352: .info("GenerateProjectionsExpander publishing expansion "
353: + dfPlugin.getClusterId());
354: }
355: dfPlugin.publishAdd(expansion);
356: Iterator subtasksIT = subtasks.iterator();
357: while (subtasksIT.hasNext()) {
358: dfPlugin.publishAdd(subtasksIT.next());
359: }
360: }
361:
362: protected void addToAndPublishExpansion(Task parent,
363: Collection subtasks) {
364: Expansion expansion = (Expansion) parent.getPlanElement();
365: NewWorkflow wf = (NewWorkflow) expansion.getWorkflow();
366: Iterator subtasksIT = subtasks.iterator();
367: while (subtasksIT.hasNext()) {
368: Task task = (Task) subtasksIT.next();
369: wf.addTask(task);
370: ((NewTask) task).setWorkflow(wf);
371: dfPlugin.publishAdd(task);
372: }
373: dfPlugin.publishChange(expansion);
374: }
375:
376: /** @deprecated */
377: protected NewTask createProjectSupplyTask(Task parentTask,
378: Asset consumer, Asset consumedItem, long start, long end,
379: Rate rate) {
380: ObjectScheduleElement rse = new ObjectScheduleElement(start,
381: end, rate);
382: ScheduleImpl rate_schedule = new ScheduleImpl();
383: rate_schedule
384: .setScheduleElementType(PlanScheduleElementType.OBJECT);
385: rate_schedule.setScheduleType(ScheduleType.OTHER);
386: rate_schedule.setScheduleElements(Collections.singleton(rse));
387: return createProjectSupplyTask(parentTask, consumer,
388: consumedItem, rate_schedule);
389: }
390:
391: protected NewTask createProjectSupplyTask(Task parentTask,
392: Asset consumer, Asset consumedItem, Schedule rate_schedule) {
393: //logger.info("GenerateProjectionsExpander create ProjectSupply Task " + dfPlugin.getClusterId());
394: NewTask newTask = getPlanningFactory().newTask();
395: newTask.setParentTask(parentTask);
396: newTask.setPlan(parentTask.getPlan());
397: newTask.setDirectObject(consumedItem);
398: newTask.setVerb(Verb.get(Constants.Verb.PROJECTSUPPLY));
399: // start and end from schedule element
400: long start = rate_schedule.getStartTime();
401: long end = rate_schedule.getEndTime();
402: //newTask.setCommitmentDate(new Date(start));
403: Vector prefs = new Vector(2);
404: prefs.addElement(getTaskUtils().createTimePreference(start,
405: dfPlugin.getLogOPlanStartTime(),
406: dfPlugin.getLogOPlanEndTime(), AspectType.START_TIME,
407: dfPlugin.getClusterId(), getPlanningFactory(), null));
408: prefs.addElement(getTaskUtils().createTimePreference(end,
409: dfPlugin.getLogOPlanStartTime(),
410: dfPlugin.getLogOPlanEndTime(), AspectType.END_TIME,
411: dfPlugin.getClusterId(), getPlanningFactory(), null));
412:
413: newTask.setPreferences(prefs.elements());
414: Vector childPhrases = createPrepPhrases(consumer, parentTask,
415: end, rate_schedule);
416: newTask.setPrepositionalPhrases(childPhrases.elements());
417:
418: return newTask;
419: }
420:
421: /**
422: * Build a workflow from a vector of tasks.
423: * @param parent parent task of workflow
424: * @param subtasks workflow tasks
425: * @return Workflow
426: **/
427: public Workflow buildWorkflow(Task parent, Collection subtasks) {
428: NewWorkflow wf = getPlanningFactory().newWorkflow();
429: wf.setParentTask(parent);
430: wf.setIsPropagatingToSubtasks(true);
431: NewTask t;
432: Iterator subtasksIT = subtasks.iterator();
433: while (subtasksIT.hasNext()) {
434: t = (NewTask) subtasksIT.next();
435: t.setWorkflow(wf);
436: wf.addTask(t);
437: }
438: return wf;
439: }
440:
441: protected PrepositionalPhrase newPrepositionalPhrase(
442: String preposition, Object io) {
443: NewPrepositionalPhrase pp = getPlanningFactory()
444: .newPrepositionalPhrase();
445: pp.setPreposition(preposition);
446: pp.setIndirectObject(io);
447: return pp;
448: }
449:
450: public Collection getConsumed(PropertyGroup pg) {
451: Collection preds = null;
452: Class parameters[] = {};
453: Object arguments[] = {};
454: Method m = null;
455: try {
456: m = dfPlugin.getSupplyClassPG().getMethod("getConsumed",
457: parameters);
458: } catch (NoSuchMethodException e) {
459: e.printStackTrace();
460: } catch (SecurityException e) {
461: e.printStackTrace();
462: }
463: try {
464: preds = (Collection) m.invoke(pg, arguments);
465: return preds;
466: } catch (IllegalAccessException e) {
467: e.printStackTrace();
468: } catch (IllegalArgumentException e) {
469: e.printStackTrace();
470: } catch (InvocationTargetException e) {
471: e.printStackTrace();
472: }
473: return new ArrayList();
474: }
475:
476: public Rate getRate(PropertyGroup pg, Asset consumedItem,
477: List params) {
478: Rate rate = null;
479: Class parameters[] = { Asset.class, List.class };
480: Object arguments[] = { consumedItem, params };
481: Method m = null;
482: try {
483: m = dfPlugin.getSupplyClassPG().getMethod("getRate",
484: parameters);
485: } catch (NoSuchMethodException e) {
486: e.printStackTrace();
487: } catch (SecurityException e) {
488: e.printStackTrace();
489: }
490: try {
491: rate = (Rate) m.invoke(pg, arguments);
492: return rate;
493: } catch (IllegalAccessException e) {
494: e.printStackTrace();
495: } catch (IllegalArgumentException e) {
496: e.printStackTrace();
497: } catch (InvocationTargetException e) {
498: e.printStackTrace();
499: }
500: return null;
501: }
502:
503: public void updateAllocationResults(Collection planElements) {
504: Iterator peIt = planElements.iterator();
505: while (peIt.hasNext()) {
506: PlanElement pe = (PlanElement) peIt.next();
507: if (PluginHelper.updatePlanElement(pe)) {
508: dfPlugin.publishChange(pe);
509: }
510: }
511: }
512:
513: /**
514: * Reconcile an intended schedule of projections with the
515: * currently published schedule of projections so as to reuse as
516: * many of the existing projection tasks as possible.
517: *
518: * Generally as elements from the published schedule are used they
519: * are removed from the schedule. Tasks remaining in the schedule
520: * are rescinded.
521: *
522: * There are three regions of interest: before now, around now and
523: * after now. These are each handled separately. In the region
524: * before now, already published tasks are unconditionally
525: * retained and new tasks are unconditionally ignored.
526: *
527: * In the region around now, tasks may start before now and end
528: * after. If both a published task and a new task spanning now
529: * exist, then there are two cases: If the demand rates are the
530: * same, then the published task is changed to look like the new
531: * task (by changing its end time preference). The start time of
532: * the published task is unchanged. Think of the existing task
533: * ending now and the new task starting now and then splicing the
534: * two together into one task. If the rates are different, then
535: * the existing task must end when the new task starts. The
536: * current code accomplishes this by setting the end time
537: * preference of the existing task to the start time of the new.
538: * This is not exactly correct since we shouldn't change the past.
539: * The times of the tasks should be no less than now.
540: *
541: * In the region after now, we try to match up the tasks. When a
542: * match is possible, the existing task is changed if necessary
543: * (and republished) otherwise it is rescinded and the new task
544: * added.
545: **/
546: protected Collection diffProjections(Schedule published_schedule,
547: Schedule newtask_schedule, TimeSpan timespan) {
548: // Only diff new projections against published tasks during this TimeSpan
549: published_schedule = ScheduleUtils.trimObjectSchedule(
550: published_schedule, timespan);
551:
552: // Check for an empty schedule
553: if (newtask_schedule.isEmpty()) {
554: // Rescind any tasks that were not accounted for
555: if (logger.isDebugEnabled()) {
556: logger
557: .debug("publishChangeProjection(), New Task Schedule empty: "
558: + newtask_schedule);
559: }
560: Enumeration e = published_schedule.getAllScheduleElements();
561: while (e.hasMoreElements()) {
562: Task task = (Task) ((ObjectScheduleElement) e
563: .nextElement()).getObject();
564: if (logger.isDebugEnabled()) {
565: logger.debug(printProjection(
566: "********** Removing task --> \n", task));
567: }
568: publishRemoveFromExpansion(task);
569: }
570: return Collections.EMPTY_LIST;
571: }
572:
573: List add_tasks = new ArrayList();
574: // Remove from the published schedule of tasks all tasks that occur BEFORE now but not overlapping now
575: // These historical tasks should not be changed
576: //TODO: MWD warning pug getStartOfPeriod here in diffProjections - not sure if should be done.
577: long now = dfPlugin.getStartOfPeriod(dfPlugin
578: .currentTimeMillis());
579:
580: ObjectScheduleElement ose;
581: Iterator historical_tasks = published_schedule
582: .getEncapsulatedScheduleElements(TimeSpan.MIN_VALUE,
583: now).iterator();
584: while (historical_tasks.hasNext()) {
585: ose = (ObjectScheduleElement) historical_tasks.next();
586: ((NewSchedule) published_schedule)
587: .removeScheduleElement(ose);
588: }
589:
590: // Examine the new task and published task that straddle NOW
591: Task published_task = null;
592: Task new_task = null;
593: //Remove historical tasks that don't touch now first.
594: Iterator new_historical_tasks = newtask_schedule
595: .getEncapsulatedScheduleElements(TimeSpan.MIN_VALUE,
596: now).iterator();
597: while (new_historical_tasks.hasNext()) {
598: ose = (ObjectScheduleElement) new_historical_tasks.next();
599: ((NewSchedule) newtask_schedule).removeScheduleElement(ose);
600: if (logger.isDebugEnabled()) {
601: logger
602: .debug("Found NEW Historical task... current society time is:"
603: + new Date(now)
604: + " start time of ose is: "
605: + new Date(ose.getStartTime())
606: + " end time of ose is: "
607: + new Date(ose.getEndTime()));
608: }
609: }
610: Collection c = newtask_schedule
611: .getScheduleElementsWithTime(now);
612: if (!c.isEmpty()) {
613: ose = (ObjectScheduleElement) c.iterator().next();
614: new_task = (Task) ose.getObject();
615: ((NewSchedule) newtask_schedule).removeScheduleElement(ose);
616: }
617: c = published_schedule.getScheduleElementsWithTime(now);
618: if (!c.isEmpty()) {
619: ose = (ObjectScheduleElement) c.iterator().next();
620: published_task = (Task) ose.getObject();
621: ((NewSchedule) published_schedule)
622: .removeScheduleElement(ose);
623: }
624: if (published_task != null && new_task != null) {
625: // Depending upon whether the rate is equal set the end time of the published task to the start or
626: // end time of the new task
627: //
628: // FIXME compare rate schedules, publish change w/ change report instead of
629: // replacing the entire task. we may need the report to say the old rate_sched, or
630: // maybe the UAInvPi can examine its buckets and figure this out. the important point
631: // is that we don't want to redo out our entire plan on the slightest orgAct change.
632: logger.warn("TODO: compare rate schedules on old task "
633: + published_task.getUID() + " and new task "
634: + new_task.getUID());
635: Rate new_rate = getTaskUtils().getRate(new_task);
636: if (new_rate.equals(getTaskUtils().getRate(published_task))) {
637: // check end times not the same
638: synchronized (new_task) {
639: ((NewTask) published_task).setPreference(new_task
640: .getPreference(AspectType.END_TIME));
641: if (getTaskUtils().getStartTime(published_task) == getTaskUtils()
642: .getEndTime(published_task)) {
643: if (logger.isWarnEnabled()) {
644: logger
645: .warn("diffProjections is setting a PUBLISHED task where the start time equals the end time "
646: + new Date(
647: getTaskUtils()
648: .getStartTime(
649: published_task))
650: + " the current society time is -> "
651: + new Date(
652: dfPlugin
653: .getCurrentTimeMillis())
654: + "\nPublished task -> "
655: + published_task
656: + "\n New task -> "
657: + new_task);
658: } // synch
659: }
660: }
661:
662: if (logger.isDebugEnabled()) {
663: logger.debug(printProjection("extend old end",
664: published_task));
665: }
666: dfPlugin.publishChange(published_task);
667: } else {
668: // check to make sure start_time is not before now
669: // long that is the maximum of now and the start_time
670: long when = Math.max(now, getTaskUtils().getStartTime(
671: new_task));
672: setEndTimePreference((NewTask) published_task, when);
673: if (getTaskUtils().getStartTime(published_task) == getTaskUtils()
674: .getEndTime(published_task)) {
675: if (logger.isWarnEnabled()) {
676: logger
677: .warn("diffProjections is setting a PUBLISHED task where the start time equals the end time "
678: + new Date(getTaskUtils()
679: .getStartTime(
680: published_task))
681: + "\nPublished task -> "
682: + published_task
683: + " the current society time is -> "
684: + new Date(dfPlugin
685: .getCurrentTimeMillis())
686: + "\n New task -> " + new_task);
687: }
688: }
689: if (logger.isDebugEnabled()) {
690: logger.debug(printProjection("truncate old end 1",
691: published_task));
692: }
693: dfPlugin.publishChange(published_task);
694: setStartTimePreference((NewTask) new_task, when);
695: if (getTaskUtils().getStartTime(new_task) == getTaskUtils()
696: .getEndTime(new_task)) {
697: if (logger.isWarnEnabled()) {
698: logger
699: .warn("diffProjections is setting a NEW task where the start time equals the end time "
700: + new Date(getTaskUtils()
701: .getStartTime(new_task))
702: + "\nPublished task -> "
703: + published_task
704: + " the current society time is -> "
705: + new Date(dfPlugin
706: .getCurrentTimeMillis())
707: + "\n New task -> " + new_task);
708: }
709: }
710: if (logger.isDebugEnabled()) {
711: logger.debug(printProjection(
712: "truncate new start 1", new_task));
713: }
714: add_tasks.add(new_task);
715: }
716: } else if (new_task != null) {
717: setStartTimePreference((NewTask) new_task, now);
718: if (getTaskUtils().getStartTime(new_task) == getTaskUtils()
719: .getEndTime(new_task)) {
720: if (logger.isWarnEnabled()) {
721: logger
722: .warn("diffProjections is setting a NEW task where the start time equals the end time "
723: + new Date(getTaskUtils()
724: .getStartTime(new_task))
725: + "\nPublished task -> "
726: + published_task
727: + " the current society time is -> "
728: + new Date(dfPlugin
729: .getCurrentTimeMillis())
730: + "\n New task -> " + new_task);
731: }
732: }
733: if (logger.isDebugEnabled()) {
734: logger.debug(printProjection("truncate new start 2",
735: new_task));
736: }
737: add_tasks.add(new_task);
738: } else if (published_task != null) {
739: setEndTimePreference((NewTask) published_task, now);
740: if (getTaskUtils().getStartTime(published_task) == getTaskUtils()
741: .getEndTime(published_task)) {
742: if (logger.isWarnEnabled()) {
743: logger
744: .warn("diffProjections is setting a PUBLISHED task where the start time equals the end time "
745: + new Date(getTaskUtils()
746: .getStartTime(
747: published_task))
748: + "\nPublished task -> "
749: + published_task
750: + " the current society time is -> "
751: + new Date(dfPlugin
752: .getCurrentTimeMillis())
753: + "\n New task -> " + new_task);
754: }
755: }
756: dfPlugin.publishChange(published_task);
757: if (logger.isDebugEnabled()) {
758: logger.debug(printProjection("truncate old end 2",
759: published_task));
760: }
761: }
762:
763: // Compare new tasks to previously scheduled tasks, if a published task is found that
764: // spans the new task's start time then adjust the published task (if needed) and publish
765: // the change. If no task is found than add new_task to list of tasks to be published.
766: // When start time of schedule is equal to TimeSpan.MIN_VALUE, schedule is empty
767: long start;
768: while (!newtask_schedule.isEmpty()) {
769: start = newtask_schedule.getStartTime();
770: ose = (ObjectScheduleElement) ScheduleUtils
771: .getElementWithTime(newtask_schedule, start);
772: if (ose != null) {
773: new_task = (Task) ose.getObject();
774: ((NewSchedule) newtask_schedule)
775: .removeScheduleElement(ose);
776: } else {
777: if (logger.isErrorEnabled()) {
778: logger
779: .error("publishChangeProjection(), Bad Schedule: "
780: + newtask_schedule);
781: }
782: return Collections.EMPTY_LIST;
783: }
784: // Get overlapping schedule elements from start to end of new task
785: c = published_schedule.getScheduleElementsWithTime(start);
786: if (!c.isEmpty()) {
787: // change the task to look like new task
788: ose = (ObjectScheduleElement) c.iterator().next();
789: published_task = (Task) ose.getObject();
790: ((NewSchedule) published_schedule)
791: .removeScheduleElement(ose);
792:
793: if (logger.isDebugEnabled()) {
794: logger.debug(" Comparing plublished task "
795: + dfPlugin.getTaskUtils().taskDesc(
796: published_task)
797: + " with \n"
798: + dfPlugin.getTaskUtils()
799: .taskDesc(new_task));
800: }
801: published_task = getTaskUtils().changeTask(
802: published_task, new_task);
803: if (published_task != null) {
804: if (getTaskUtils().getStartTime(published_task) == getTaskUtils()
805: .getEndTime(published_task)) {
806: if (logger.isWarnEnabled()) {
807: logger
808: .warn("diffProjections is setting a PUBLISHED task where the start time equals the end time "
809: + new Date(
810: getTaskUtils()
811: .getStartTime(
812: published_task))
813: + "\nPublished task -> "
814: + published_task
815: + "\n New task -> "
816: + new_task);
817: }
818: }
819: if (logger.isDebugEnabled()) {
820: logger
821: .debug(printProjection(
822: "********** Replaced task with ---> \n",
823: published_task));
824: }
825: dfPlugin.publishChange(published_task);
826: }
827: } else {
828: // no task exists that covers this timespan, publish it
829: add_tasks.add(new_task);
830: if (getTaskUtils().getStartTime(new_task) == getTaskUtils()
831: .getEndTime(new_task)) {
832: if (logger.isWarnEnabled()) {
833: logger
834: .warn("diffProjections is setting a NEW task where the start time equals the end time "
835: + new Date(getTaskUtils()
836: .getStartTime(new_task))
837: + "\nPublished task -> "
838: + published_task
839: + " the current society time is -> "
840: + new Date(dfPlugin
841: .getCurrentTimeMillis())
842: + "\n New task -> " + new_task);
843: }
844: }
845: }
846: }
847: // Rescind any tasks that were not accounted for
848: Enumeration e = published_schedule.getAllScheduleElements();
849: while (e.hasMoreElements()) {
850: Task task = (Task) ((ObjectScheduleElement) e.nextElement())
851: .getObject();
852: if (logger.isDebugEnabled()) {
853: logger.debug(printProjection(
854: "********** Removing task --> \n", task));
855: }
856: publishRemoveFromExpansion(task);
857: }
858: return add_tasks;
859: }
860:
861: protected void setStartTimePreference(NewTask task, long start) {
862: task.setPreference(getTaskUtils().createTimePreference(start,
863: dfPlugin.getLogOPlanStartTime(),
864: dfPlugin.getLogOPlanEndTime(), AspectType.START_TIME,
865: dfPlugin.getClusterId(), getPlanningFactory(), null));
866: }
867:
868: protected void setEndTimePreference(NewTask task, long end) {
869: task.setPreference(getTaskUtils().createTimePreference(end,
870: dfPlugin.getLogOPlanStartTime(),
871: dfPlugin.getLogOPlanEndTime(), AspectType.END_TIME,
872: dfPlugin.getClusterId(), getPlanningFactory(), null));
873: }
874:
875: protected String printProjection(String msg, Task task) {
876: return "diffProjections() "
877: + task.getUID()
878: + " "
879: + msg
880: + " "
881: + getTaskUtils().getDailyQuantity(task)
882: + " "
883: + getTimeUtils().dateString(
884: getTaskUtils().getStartTime(task))
885: + " to "
886: + getTimeUtils().dateString(
887: getTaskUtils().getEndTime(task));
888: }
889:
890: public void publishRemoveFromExpansion(Task subtask) {
891: NewWorkflow wf = (NewWorkflow) subtask.getWorkflow();
892: if (wf != null) {
893: wf.removeTask(subtask);
894: }
895: dfPlugin.publishRemove(subtask);
896: }
897:
898: public double getDailyQuantity(Rate r) {
899: Duration d = Duration.newDays(1.0);
900: Scalar measure = (Scalar) r.computeNumerator(d);
901: double result = Double.NaN;
902: if (measure instanceof Volume) {
903: result = ((Volume) measure).getGallons();
904: } else if (measure instanceof Count) {
905: result = ((Count) measure).getEaches();
906: } else if (measure instanceof Mass) {
907: result = ((Mass) measure).getShortTons();
908: } else {
909: if (logger.isErrorEnabled()) {
910: logger.error("cannot determine type of measure");
911: }
912: }
913: return result;
914: }
915: }
|