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 java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.Collection;
032: import java.util.Vector;
033: import java.util.ArrayList;
034: import java.util.Date;
035: import java.util.List;
036:
037: import org.cougaar.planning.ldm.plan.*;
038: import org.cougaar.planning.ldm.asset.Asset;
039: import org.cougaar.planning.ldm.asset.TypeIdentificationPG;
040: import org.cougaar.planning.ldm.asset.ItemIdentificationPG;
041: import org.cougaar.logistics.ldm.Constants;
042: import org.cougaar.glm.ldm.asset.Organization;
043: import org.cougaar.glm.ldm.plan.GeolocLocation;
044: import org.cougaar.planning.ldm.plan.AspectValue;
045: import org.cougaar.planning.ldm.measure.*;
046: import org.cougaar.logistics.plugin.inventory.MaintainedItem;
047: import org.cougaar.util.Random;
048:
049: /**
050: * The DemandTaskGenerator generates Supply tasks based on the based in ProjectSupply tasks.
051: *
052: * @see DemandGeneratorPlugin
053: * @see DemandGeneratorModule
054: */
055:
056: public class DemandTaskGenerator extends DemandGeneratorModule
057: implements DemandTaskGeneratorIfc {
058:
059: protected HashMap projHash;
060: protected HashMap supplyHash;
061:
062: protected Random poissonGen;
063:
064: public DemandTaskGenerator(
065: DemandGeneratorPlugin demandGeneratorPlugin) {
066: super (demandGeneratorPlugin);
067: projHash = new HashMap();
068: supplyHash = new HashMap();
069: poissonGen = new Random();
070: }
071:
072: /**
073: * Generate actual demand tasks from the passed in projections
074: */
075:
076: public List generateDemandTasks(long start, long duration,
077: Collection relevantProjectSupplys,
078: Collection relevantSupplys) {
079: ArrayList demandTasks = new ArrayList();
080: regenerateProjectionHash(relevantProjectSupplys);
081: regenerateSupplyHash(relevantSupplys);
082: long end = start + duration;
083: Iterator gpTasksIt = projHash.keySet().iterator();
084: while (gpTasksIt.hasNext()) {
085: Task gpTask = (Task) gpTasksIt.next();
086: ArrayList supplyTasks = new ArrayList();
087: HashMap assetMap = (HashMap) projHash.get(gpTask);
088: Iterator assetsIt = assetMap.keySet().iterator();
089: while (assetsIt.hasNext()) {
090: Asset consumed = (Asset) assetsIt.next();
091: Collection projTasks = (Collection) (assetMap
092: .get(consumed));
093:
094: double totalQty = deriveTotalQty(start, start
095: + duration, projTasks);
096: if (totalQty <= 0.0) {
097: continue;
098: }
099: double taskQty = 0.0;
100: if (dgPlugin.getPoissonOn()) {
101: taskQty = poissonGen.nextPoisson(totalQty);
102: } else {
103: taskQty = totalQty;
104: }
105:
106: Iterator projTaskIt = projTasks.iterator();
107:
108: if ((taskQty > 0) && (projTaskIt.hasNext())) {
109: Task projTask = (Task) projTaskIt.next();
110:
111: double capacity = dgPlugin
112: .getCapacityForTask(projTask);
113: if (capacity > 0.0d) {
114: /** TODO eventually remove MWD
115: if(dgPlugin.getPoissonOn() && taskQty > capacity) {
116: logger.warn("Random generations at " + dgPlugin.getOrgName() + " produced a qty of " + taskQty +
117: " and totalQty " + totalQty + "also greater than capacity at " + capacity);
118: }
119: **/
120: taskQty = Math.min(taskQty, capacity);
121: }
122:
123: HashMap supplyAssetMap = (HashMap) supplyHash
124: .get(gpTask);
125: Collection issuedSupplyTasks = null;
126: if (supplyAssetMap != null) {
127: issuedSupplyTasks = (Collection) supplyAssetMap
128: .get(consumed);
129: }
130: //If a supply task hasn't already been created for this day,asset
131: //then generate the demand task
132: if ((taskQty > 0)
133: && ((issuedSupplyTasks == null) || (issuedSupplyTasks
134: .isEmpty()))) {
135: supplyTasks.add(createNewDemandTask(gpTask,
136: projTask, consumed, start, start
137: + duration, taskQty));
138: }
139: }
140: }
141: if (!supplyTasks.isEmpty()) {
142: if (addToAndPublishExpansion(gpTask, supplyTasks)) {
143: demandTasks.addAll(supplyTasks);
144: }
145: }
146: }
147:
148: return demandTasks;
149:
150: }
151:
152: protected void regenerateProjectionHash(Collection tasks) {
153: regenerateTaskHash(tasks, projHash);
154: }
155:
156: protected void regenerateSupplyHash(Collection tasks) {
157: regenerateTaskHash(tasks, supplyHash);
158: }
159:
160: protected void regenerateTaskHash(Collection tasks, HashMap hash) {
161: hash.clear();
162: Iterator taskIt = tasks.iterator();
163: while (taskIt.hasNext()) {
164: Task task = (Task) taskIt.next();
165: Workflow wf = task.getWorkflow();
166: if (wf == null) {
167: if (logger.isErrorEnabled()) {
168: logger.error("" + task.getVerb() + " task: "
169: + task.getUID() + " at "
170: + dgPlugin.getOrgName()
171: + " has Null workflow");
172: }
173: continue;
174: }
175: Task parTask = wf.getParentTask();
176: if (parTask == null) {
177: if (logger.isErrorEnabled()) {
178: logger.error("" + task.getVerb()
179: + " task with no parent");
180: }
181: continue;
182: }
183: if (!(parTask.getVerb()
184: .equals(Constants.Verb.GENERATEPROJECTIONS))) {
185: if (logger.isErrorEnabled()) {
186: logger
187: .error(""
188: + task.getVerb()
189: + " task with non generate projections parent");
190: }
191: continue;
192: }
193: HashMap assetMap = (HashMap) hash.get(parTask);
194: if (assetMap == null) {
195: assetMap = new HashMap();
196: hash.put(parTask, assetMap);
197: }
198: Asset asset = task.getDirectObject();
199: ArrayList tasksWAsset = (ArrayList) assetMap.get(asset);
200: if (tasksWAsset == null) {
201: tasksWAsset = new ArrayList();
202: assetMap.put(asset, tasksWAsset);
203: }
204: tasksWAsset.add(task);
205: }
206: }
207:
208: protected double deriveTotalQty(long bucketStart, long bucketEnd,
209: Collection projTasks) {
210: Iterator tasksIt = projTasks.iterator();
211: double totalQty = 0.0;
212: while (tasksIt.hasNext()) {
213: Task projTask = (Task) tasksIt.next();
214: long taskStart = getTaskUtils().getStartTime(projTask);
215: long taskEnd = getTaskUtils().getEndTime(projTask);
216:
217: /*
218: if (taskStart > bucketEnd || taskEnd < bucketStart) {
219: continue;
220: }
221: */
222:
223: long start = Math.max(taskStart, bucketStart);
224: long end = Math.min(taskEnd, bucketEnd);
225: //duration in seconds
226: double duration = ((end - start) / 1000);
227: Rate rate = getTaskUtils().getRate(projTask, start, end);
228: double qty = (getBaseUnitPerSecond(rate) * duration);
229: totalQty += qty;
230: }
231: return totalQty;
232: }
233:
234: protected double getBaseUnitPerSecond(Rate rate) {
235: if (rate instanceof CostRate) {
236: return ((CostRate) rate).getDollarsPerSecond();
237: } else if (rate instanceof CountRate) {
238: return ((CountRate) rate).getEachesPerSecond();
239: } else if (rate instanceof FlowRate) {
240: return ((FlowRate) rate).getGallonsPerSecond();
241: } else if (rate instanceof MassTransferRate) {
242: return ((MassTransferRate) rate).getShortTonsPerSecond();
243: } else if (rate instanceof TimeRate) {
244: return ((TimeRate) rate).getHoursPerSecond();
245: } // if
246: return 0.0;
247: }
248:
249: //Method to check if children in hash still on blackboard if parent gp
250: //hash a disposition
251: protected void checkForOrphans(Task gpTask) {
252: HashMap assetMap = (HashMap) projHash.get(gpTask);
253: Iterator assetsIt = assetMap.keySet().iterator();
254: while (assetsIt.hasNext()) {
255: Asset consumed = (Asset) assetsIt.next();
256: Collection projTasks = (Collection) (assetMap.get(consumed));
257: Iterator taskIt = projTasks.iterator();
258: while (taskIt.hasNext()) {
259: Task projTask = (Task) taskIt.next();
260: if (dgPlugin.checkIfTaskOnBlackboard(projTask)) {
261: if (logger.isErrorEnabled()) {
262: logger
263: .error("Parent task has Disposition. Orphaned task = "
264: + getTaskUtils().taskDesc(
265: projTask));
266: }
267: }
268: }
269: }
270: }
271:
272: protected boolean addToAndPublishExpansion(Task parent,
273: Collection subtasks) {
274: Expansion expansion = null;
275: try {
276: expansion = (Expansion) parent.getPlanElement();
277: } catch (ClassCastException ex) {
278: if (logger.isErrorEnabled()) {
279: logger
280: .error(
281: "ClassCastException. Unexpected disposition on gpTask that was found as parent in ProjectSupplies. Bug# 13670 GP Task = "
282: + parent, ex);
283: checkForOrphans(parent);
284: return false;
285: }
286: }
287: NewWorkflow wf = (NewWorkflow) expansion.getWorkflow();
288: Iterator subtasksIT = subtasks.iterator();
289: //TODO: MWD Remove debug statements:
290: if ((dgPlugin.getOrgName() != null)
291: && (dgPlugin.getOrgName().trim().equals("1-35-ARBN"))
292: && (logger.isDebugEnabled())) {
293: logger.debug("DGPlugin:DemandTaskGenerator:I'm publishing "
294: + subtasks.size() + " " + dgPlugin.getSupplyType()
295: + " Supply tasks");
296: }
297: while (subtasksIT.hasNext()) {
298: Task task = (Task) subtasksIT.next();
299: wf.addTask(task);
300: ((NewTask) task).setWorkflow(wf);
301: dgPlugin.publishAdd(task);
302: }
303: dgPlugin.publishChange(expansion);
304:
305: return true;
306: }
307:
308: protected NewTask createNewDemandTask(Task parentTask,
309: Task projTask, Asset consumed, long start, long end,
310: double qty) {
311:
312: Vector prefs = createDemandPreferences(start, end, qty);
313:
314: NewTask newTask = getPlanningFactory().newTask();
315:
316: newTask.setParentTask(parentTask);
317: newTask.setPlan(parentTask.getPlan());
318:
319: //TODO: MWD Remove
320: //newTask.setPrepositionalPhrases(parentTask.getPrepositionalPhrases());
321: //newTask = addPrepositionalPhrase(newTask, prepPhrase);
322: //TODO: MWD Remove - also remove createDemandPrepPhrases
323: //Vector preps = createDemandPrepPhrases(parentTask, consumed, end);
324: //newTask.setPrepositionalPhrases(preps.elements());
325:
326: newTask.setPrepositionalPhrases(projTask
327: .getPrepositionalPhrases());
328:
329: newTask.setDirectObject(consumed);
330: newTask.setVerb(Verb.get(Constants.Verb.SUPPLY));
331:
332: newTask.setPreferences(prefs.elements());
333:
334: //bug#2974
335: newTask.setContext(parentTask.getContext());
336:
337: newTask.setCommitmentDate(new Date(end));
338:
339: return newTask;
340: }
341:
342: /**
343: * Create FOR, TO, MAINTAIN, and OFTYPE prepositional phrases
344: * for use by the subclasses.
345: * @param parentTask - The parent GP Task
346: * @param resource
347: * @param time - used to find the OPlan and the geoloc for the TO preposition
348: * @return Vector of PrepostionalPhrases
349: *
350: **/
351:
352: /***
353: * TODO: MWD Remove
354: *
355:
356: protected Vector createDemandPrepPhrases
357: (Task parentTask, Asset resource, long time) {
358:
359: Asset consumer = parentTask.getDirectObject();
360:
361: // Create prepositions for the new demand task
362: Vector pp_vector = new Vector();
363: PrepositionalPhrase pp =
364: parentTask.getPrepositionalPhrase(Constants.Preposition.OFTYPE);
365:
366: if (pp != null) {
367: Object obj = pp.getIndirectObject();
368: if (obj instanceof String) {
369: pp_vector.addElement(newPrepositionalPhrase
370: (Constants.Preposition.OFTYPE, obj));
371: }
372: }
373:
374: pp_vector.addElement(newPrepositionalPhrase
375: (Constants.Preposition.FOR, dgPlugin.getOrgName()));
376:
377: // when oplan id added to tasks....
378: // pp_vector.addElement (newPrepositionalPhrase (Constants.Preposition.FOROPLAN, oplan));
379:
380: GeolocLocation geoloc = getGeolocLocation(time, dgPlugin.getMyOrganization());
381: if (geoloc != null) {
382: pp_vector.addElement(newPrepositionalPhrase(Constants.Preposition.TO, geoloc));
383: } else {
384: // Try to use HomeLocation
385: try {
386: geoloc = (GeolocLocation)
387: dgPlugin.getMyOrganization().getMilitaryOrgPG().getHomeLocation();
388: pp_vector.addElement
389: (newPrepositionalPhrase(Constants.Preposition.TO, geoloc));
390: } catch (NullPointerException npe) {
391: if(logger.isErrorEnabled()) {
392: logger.error("Geoloc not found");
393: }
394: }
395: }
396:
397: if (consumer != null) {
398: MaintainedItem itemID;
399: if (consumer instanceof Asset) {
400: TypeIdentificationPG tip = ((Asset) consumer).getTypeIdentificationPG();
401: ItemIdentificationPG iip = ((Asset) consumer).getItemIdentificationPG();
402: if (iip != null) {
403: itemID = MaintainedItem.findOrMakeMaintainedItem
404: ("Asset", tip.getTypeIdentification(), iip.getItemIdentification(),
405: tip.getNomenclature(),dgPlugin);
406: } else {
407: itemID = MaintainedItem.findOrMakeMaintainedItem
408: ("Asset", tip.getTypeIdentification(), null, tip.getNomenclature(), dgPlugin);
409: }
410: } else {
411: itemID = MaintainedItem.findOrMakeMaintainedItem
412: ("Other", consumer.toString(), null, null,dgPlugin);
413: }
414: pp_vector.addElement(newPrepositionalPhrase
415: (Constants.Preposition.MAINTAINING, itemID));
416: }
417:
418: return pp_vector;
419: }
420:
421:
422: public PrepositionalPhrase newPrepositionalPhrase(String preposition,
423: Object io) {
424: NewPrepositionalPhrase pp = getPlanningFactory().newPrepositionalPhrase();
425: pp.setPreposition(preposition);
426: pp.setIndirectObject(io);
427: return pp;
428: }
429: **/
430:
431: /**
432: * @return Geographic location of this organization at specifed time.
433: * TODO: MWD Remove
434: * *
435: * public GeolocLocation getGeolocLocation(long time, Organization myOrg) {
436: * Enumeration geolocs =
437: * getAssetUtils().getGeolocLocationAtTime(myOrg, time);
438: * if (geolocs.hasMoreElements()) {
439: * return ((GeolocLocation) geolocs.nextElement());
440: * }
441: * return null;
442: * }
443: */
444:
445: protected Vector createDemandPreferences(long start, long end,
446: double qty) {
447: Vector prefs = new Vector();
448:
449: //Took out START_TIME preference on 6/3/2003 as they seemed like misleading
450: //superfluous information that nobody else counts on. Nominally a Supply
451: //task doesn't have a start time. Didn't want to include it so that
452: //in the future someone may rely upon it. If this Changes you have
453: //to tell PSU
454:
455: //Whoops PSU currently rely's on the start time. Which is a problem
456: //For the time being reintroduce it. Talked To Ray
457:
458: prefs.addElement(createTimeScoringFunctionPref(start,
459: AspectType.START_TIME));
460: prefs.addElement(createTimeScoringFunctionPref(end,
461: AspectType.END_TIME));
462:
463: AspectValue av = AspectValue.newAspectValue(
464: AspectType.QUANTITY, qty);
465: ScoringFunction score = ScoringFunction
466: .createStrictlyAtValue(av);
467: prefs.addElement(getPlanningFactory().newPreference(
468: AspectType.QUANTITY, score));
469:
470: return prefs;
471: }
472:
473: protected Preference createTimeScoringFunctionPref(long bestTime,
474: int aspectType) {
475:
476: long bucketStart = dgPlugin.getStartOfPeriod(dgPlugin
477: .getCurrentTimeMillis());
478: long bucketEnd = bucketStart + dgPlugin.getPeriod();
479: long earliestTime = dgPlugin.getLogOPlanStartTime();
480: // Get the later of now and org report time
481: if (bucketStart > dgPlugin.getLogOPlanStartTime()) {
482: earliestTime = bucketStart;
483: }
484: long theBadPast = bucketStart - 1;
485: long latestTime = dgPlugin.getLogOPlanEndTime();
486: long firstLateTime = bestTime + dgPlugin.getPeriod();
487:
488: double bucketsBetween = ((latestTime - bestTime) / dgPlugin
489: .getPeriod()) - 1;
490: //Use .0033 as a slope for now
491: double lateScore = .0033 * bucketsBetween;
492: // use some baseline score for earlier or 1 day late
493: double alpha = .25;
494: Vector points = new Vector();
495:
496: AspectScorePoint badest = new AspectScorePoint(AspectValue
497: .newAspectValue(aspectType, theBadPast), 1.0);
498: AspectScorePoint earliest = new AspectScorePoint(AspectValue
499: .newAspectValue(aspectType, earliestTime), alpha);
500: AspectScorePoint best = new AspectScorePoint(AspectValue
501: .newAspectValue(aspectType, bestTime), 0.0);
502: AspectScorePoint first_late = new AspectScorePoint(AspectValue
503: .newAspectValue(aspectType, firstLateTime), alpha);
504: AspectScorePoint latest = new AspectScorePoint(AspectValue
505: .newAspectValue(aspectType, latestTime), alpha
506: + lateScore);
507:
508: points.addElement(badest);
509: points.addElement(earliest);
510: points.addElement(best);
511: if (firstLateTime < latestTime) {
512: points.addElement(first_late);
513: } else if (logger.isInfoEnabled()) {
514: // Note that this case can happen when near the end of stage 4 in the UAas its on
515: //last day of demand before the new org activity has been issued
516: logger
517: .info(dgPlugin.getOrgName()
518: + ".createTimePref skipping firstLate point: latest <= firstLate! latest: "
519: + new Date(latestTime)
520: + ", firstLate: "
521: + new Date(firstLateTime)
522: + ((bestTime == latestTime && aspectType == AspectType.END_TIME) ? ". A Task EndPref where bestTime==OplanEnd."
523: : ". AspectType: " + aspectType));
524: }
525: points.addElement(latest);
526:
527: ScoringFunction score = ScoringFunction
528: .createPiecewiseLinearScoringFunction(points.elements());
529:
530: //ScoringFunction score = ScoringFunction.createStrictlyAtValue(AspectValue.newAspectValue(aspectType, bestTime));
531:
532: return getPlanningFactory().newPreference(aspectType, score);
533: }
534:
535: }
|