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: package org.cougaar.logistics.plugin.utils;
027:
028: import org.cougaar.core.logging.NullLoggingServiceImpl;
029: import org.cougaar.glm.ldm.GLMFactory;
030: import org.cougaar.glm.ldm.asset.NewScheduledContentPG;
031: import org.cougaar.glm.ldm.asset.PropertyGroupFactory;
032: import org.cougaar.glm.ldm.asset.ScheduledContentPG;
033: import org.cougaar.glm.ldm.oplan.OrgActivity;
034: import org.cougaar.glm.ldm.plan.NewQuantityScheduleElement;
035: import org.cougaar.glm.ldm.plan.QuantityScheduleElement;
036: import org.cougaar.glm.ldm.plan.ObjectScheduleElement;
037: import org.cougaar.glm.ldm.plan.PlanScheduleElementType;
038: import org.cougaar.glm.ldm.plan.PlanScheduleType;
039: import org.cougaar.glm.ldm.plan.QuantityScheduleElement;
040: import org.cougaar.logistics.plugin.inventory.TimeUtils;
041: import org.cougaar.logistics.plugin.inventory.UtilsProvider;
042: import org.cougaar.planning.ldm.asset.AggregateAsset;
043: import org.cougaar.planning.ldm.asset.Asset;
044: import org.cougaar.planning.ldm.plan.Schedule;
045: import org.cougaar.planning.ldm.plan.ScheduleElement;
046: import org.cougaar.planning.ldm.plan.ScheduleImpl;
047: import org.cougaar.planning.ldm.plan.ScheduleType;
048: import org.cougaar.planning.ldm.plan.ScheduleUtilities;
049: import org.cougaar.util.TimeSpan;
050: import org.cougaar.util.log.Logger;
051:
052: import java.util.Collection;
053: import java.util.Enumeration;
054: import java.util.Iterator;
055: import java.util.Vector;
056:
057: /** Provide convenience methods for creating objects. */
058: public class ScheduleUtils {
059:
060: protected static final long MSEC_PER_DAY = 86400000;
061: protected static final long SECOND_IN_MS = 1000;
062:
063: private transient Logger logger;
064:
065: public ScheduleUtils(UtilsProvider provider) {
066: if (provider == null) {
067: logger = NullLoggingServiceImpl.getLoggingService();
068: } else {
069: logger = (Logger) provider.getLoggingService(this );
070: }
071: }
072:
073: public static Vector convertEnumToVector(Enumeration e) {
074: Vector v = new Vector();
075: while (e.hasMoreElements()) {
076: v.addElement(e.nextElement());
077: }
078: return v;
079: }
080:
081: public static ScheduledContentPG createScheduledContentPG(Asset a,
082: Schedule s) {
083: NewScheduledContentPG scp = PropertyGroupFactory
084: .newScheduledContentPG();
085: scp.setAsset(a);
086: scp.setSchedule(s);
087: return scp;
088: }
089:
090: // truncates time span
091: // note amount can be positive or negative.
092: public static Schedule adjustSchedule(Schedule sched, long start,
093: long end, int amount, long time_incr) {
094: return adjustSchedule(sched, truncTime(start, time_incr),
095: truncTime(end + 1, time_incr) - 1, amount);
096: }
097:
098: // note amount can be positive or negative.
099: public static Schedule adjustSchedule(Schedule sched, long start,
100: long end, int amount) {
101: Schedule simple_sched = buildSimpleQuantitySchedule(amount,
102: start, end);
103: return ScheduleUtilities.addSchedules(sched, simple_sched);
104: }
105:
106: public static Schedule buildSimpleQuantitySchedule(double qty,
107: long start, long end) {
108: QuantityScheduleElement nqse = buildQuantityScheduleElement(
109: qty, start, end);
110: Vector sched_el = new Vector();
111: sched_el.addElement(nqse);
112:
113: return GLMFactory.newQuantitySchedule(sched_el.elements(),
114: PlanScheduleType.TOTAL_INVENTORY);
115: }
116:
117: public static Schedule buildSimpleQuantitySchedule(int qty,
118: long start, long end, long time_incr) {
119: return buildSimpleQuantitySchedule(qty, truncTime(start,
120: time_incr), truncTime(end + 1, time_incr) - 1);
121: }
122:
123: public static long truncTime(long time, long time_incr) {
124: return (time / time_incr) * time_incr;
125: }
126:
127: public static QuantityScheduleElement buildQuantityScheduleElement(
128: double qty, long start, long end) {
129: NewQuantityScheduleElement e = GLMFactory
130: .newQuantityScheduleElement();
131: e.setQuantity(qty);
132: e.setStartTime(start);
133: e.setEndTime(end);
134: return e;
135: }
136:
137: public static QuantityScheduleElement buildQuantityScheduleElement(
138: double qty, long start, long end, long time_incr) {
139: return buildQuantityScheduleElement(qty, truncTime(start,
140: time_incr), truncTime(end + 1, time_incr) - 1);
141: }
142:
143: // note amount can be positive or negative.
144: // adjust quantity from start until end of schedule
145: // if start is after end of schedule,
146: // append new element from start to start + a day
147: public static Schedule adjustSchedule(Schedule sched, long start,
148: int amount) {
149: long end = sched.getEndTime();
150: if (end > start)
151: end = start + MSEC_PER_DAY;
152: return adjustSchedule(sched, start, end, amount);
153: }
154:
155: public static boolean isOffendingSchedule(Schedule sched) {
156: long start = sched.getStartTime() - 1;
157: QuantityScheduleElement qse;
158: Enumeration elements = sched.getAllScheduleElements();
159: while (elements.hasMoreElements()) {
160: qse = (QuantityScheduleElement) elements.nextElement();
161: if (qse.getStartTime() < start) {
162: return true;
163: }
164: start = qse.getStartTime();
165: }
166: return false;
167: }
168:
169: public static ScheduleElement getElementWithTime(Schedule s,
170: long time) {
171: ScheduleElement se = null;
172: if (s != null) {
173: Collection c = s.getScheduleElementsWithTime(time);
174: if (!c.isEmpty()) {
175: se = (ScheduleElement) c.iterator().next();
176: }
177: }
178: return se;
179: }
180:
181: public Schedule createConsumerSchedule(Collection col) {
182: ScheduledContentPG scp;
183: int qty;
184: Asset consumer, asset;
185: Schedule consumerSched = null;
186: Iterator list = col.iterator();
187: while (list.hasNext()) {
188: asset = (Asset) list.next();
189: if (asset instanceof AggregateAsset) {
190: AggregateAsset aa = (AggregateAsset) asset;
191: qty = (int) aa.getQuantity();
192: consumer = aa.getAsset();
193: } else {
194: qty = 1;
195: consumer = asset.getPrototype();
196: }
197: if (consumer == null) {
198: if (logger.isErrorEnabled()) {
199: logger
200: .error("Missing prototype on asset: "
201: + asset);
202: }
203: continue;
204: }
205: Schedule roleSched = asset.getRoleSchedule()
206: .getAvailableSchedule();
207: if (roleSched == null) {
208: if (logger.isErrorEnabled()) {
209: logger.error("Missing RoleSchedule on asset: "
210: + asset);
211: }
212: continue;
213: }
214: long start = roleSched.getStartTime();
215: long end = roleSched.getEndTime();
216: if (start >= end - 1) {
217: if (logger.isErrorEnabled()) {
218: logger.error("Bad schedule time(s): start "
219: + TimeUtils.dateString(start) + ", end "
220: + TimeUtils.dateString(end)
221: + " for asset: " + asset);
222: }
223: continue;
224: }
225: // ScheduleUtils schedule methods have a granularity - so 1000 = 1 sec
226: // so everything is scheduled on an even second
227: if (consumerSched == null) {
228: consumerSched = buildSimpleQuantitySchedule(qty, start,
229: end, SECOND_IN_MS);
230: } else {
231: consumerSched = adjustSchedule(consumerSched, start,
232: end, qty, SECOND_IN_MS);
233: }
234: }
235: return consumerSched;
236: }
237:
238: public static Schedule newObjectSchedule(Enumeration elements) {
239: ScheduleImpl s = new ScheduleImpl();
240: s.setScheduleElementType(PlanScheduleElementType.OBJECT);
241: s.setScheduleType(ScheduleType.OTHER);
242: s.setScheduleElements(elements);
243: return s;
244: }
245:
246: /**
247: * Here is what this method does (Ray Tomlinson). Merges a number
248: * of parameter schedules into a combined schedule. In the
249: * combined schedule, the "parameter" of each element is a Vector
250: * of the parameters from each of the input schedules. For
251: * elements not covered by the input schedule, the corresponding
252: * Vector element is null. The output schedule elements correspond
253: * to the intersections of the elements or inter-element gaps of
254: * the input schedules. No schedule element is generated for time
255: * spans where none of the input schedules has an element.
256: * Conversely, all elements of the merged schedule have at least
257: * one non-null parameter.
258: *
259: * There is an assumption that the elements of the input schedules
260: * and the output schedule are non-overlapping.
261: **/
262: public Schedule getMergedSchedule(Vector parameterSchedules) {
263: // sets scheds to Enumerations of schedule elements
264: // sets intervals to the initial schedule element within each schedule
265: Schedule mergedSchedule;
266: Vector scheds = parameterSchedules;
267: int num_params = scheds.size();
268: ObjectScheduleElement[] intervals = new ObjectScheduleElement[num_params];
269: Enumeration[] enums = new Enumeration[num_params];
270: if (logger.isDebugEnabled()) {
271: logger.debug("getMergedSchedule " + num_params
272: + " num params");
273: }
274: ObjectScheduleElement ose;
275: long start = TimeSpan.MAX_VALUE;
276: for (int ii = 0; ii < num_params; ii++) {
277: enums[ii] = ((Schedule) scheds.get(ii))
278: .getAllScheduleElements();
279: if (enums[ii].hasMoreElements()) {
280: ose = (ObjectScheduleElement) enums[ii].nextElement();
281: intervals[ii] = ose;
282: if (ose.getStartTime() < start) {
283: start = ose.getStartTime();
284: }
285: } else {
286: intervals[ii] = null; // Empty schedule
287: }
288: }
289:
290: Vector result_sched = new Vector();
291: long end = TimeSpan.MIN_VALUE;
292: while (end != TimeSpan.MAX_VALUE) {
293: Vector params = new Vector(num_params);
294: params.setSize(num_params);
295: boolean haveParams = false;
296: end = TimeSpan.MAX_VALUE;
297: for (int ii = 0; ii < num_params; ii++) {
298: params.set(ii, null);// Presume no element for schedule(ii)
299: // check if interval good
300: ose = intervals[ii];
301: if (ose != null) {
302: if (ose.getEndTime() <= start) {
303: // This has already been covered; Step to next
304: if (!enums[ii].hasMoreElements()) {
305: // ran off end of schedule(ii)
306: intervals[ii] = null;
307: continue;
308: }
309: ose = (ObjectScheduleElement) enums[ii]
310: .nextElement();
311: intervals[ii] = ose;
312: }
313: if (ose.getStartTime() > start) {
314: // ose is _not_ part of this result
315: // element, it's later (there is a gap)
316: if (ose.getStartTime() < end) {
317: // This result element ends not later
318: // than the start of this pending element
319: end = ose.getStartTime();
320: }
321: continue;
322: }
323: // search for earliest end time
324: if (ose.getEndTime() < end) {
325: end = ose.getEndTime();
326: }
327: // add current param to list
328: params.set(ii, ose.getObject());
329: haveParams = true;
330: }
331: }
332: if (haveParams) {
333: result_sched.add(new ObjectScheduleElement(start, end,
334: params));
335: }
336: start = end;
337: }
338: mergedSchedule = newObjectSchedule(result_sched.elements());
339: if (logger.isDebugEnabled()) {
340: logger.debug("getMergedSchedule created mergedSchedule "
341: + result_sched.size());
342: }
343: return mergedSchedule;
344: }
345:
346: public Schedule convertQuantitySchedule(Schedule qty_sched) {
347: ObjectScheduleElement element;
348: QuantityScheduleElement qty_el;
349: Vector sched_els = new Vector();
350: Enumeration qty_els = qty_sched.getAllScheduleElements();
351: while (qty_els.hasMoreElements()) {
352: qty_el = (QuantityScheduleElement) qty_els.nextElement();
353: element = new ObjectScheduleElement(qty_el.getStartDate(),
354: qty_el.getEndDate(), new Double(qty_el
355: .getQuantity()));
356: sched_els.addElement(element);
357: }
358: Schedule result_sched = newObjectSchedule(sched_els.elements());
359: return result_sched;
360: }
361:
362: public Schedule createOrgActivitySchedule(Collection col) {
363: OrgActivity orgAct = null;
364: long start, end;
365: Schedule orgActSchedule = null;
366: Vector schedElements = new Vector();
367: if (col != null) {
368: Iterator list = col.iterator();
369: while (list.hasNext()) {
370: orgAct = (OrgActivity) list.next();
371: start = orgAct.getStartTime();
372: end = orgAct.getEndTime();
373: schedElements.add(new ObjectScheduleElement(start, end,
374: orgAct));
375: }
376: orgActSchedule = newObjectSchedule(schedElements.elements());
377: } else {
378: if (logger.isErrorEnabled()) {
379: logger
380: .error("createOrgActivitySchedule passed empty collection");
381: }
382: }
383: return orgActSchedule;
384: }
385:
386: public static Schedule trimObjectSchedule(Schedule schedule,
387: TimeSpan span) {
388: if (span == null) {
389: return schedule;
390: }
391: ObjectScheduleElement startOse = null, endOse = null, ose;
392: Vector resultElements = new Vector();
393: long start = span.getStartTime();
394: long end = span.getEndTime();
395: // System.out.println("TIME SPAN "+TimeUtils.dateString(start)+
396: // " to "+TimeUtils.dateString(end)+", SCHEDULE "+schedule);
397: // Grab all elements of this schedule that are bounded by timespan
398: Collection elements = schedule.getEncapsulatedScheduleElements(
399: start, end);
400: // Grab start and end time elements as they may not be bounded by timespan
401: Collection col = schedule.getScheduleElementsWithTime(start);
402: if (!col.isEmpty()) {
403: startOse = (ObjectScheduleElement) col.iterator().next();
404: }
405: col = schedule.getScheduleElementsWithTime(end - 1);
406: if (!col.isEmpty()) {
407: endOse = (ObjectScheduleElement) col.iterator().next();
408: }
409: Iterator elementsIt = elements.iterator();
410: while (elementsIt.hasNext()) {
411: ose = (ObjectScheduleElement) elementsIt.next();
412: // Check to see if start or end is fully encapsulated in this schedule
413: if (ose == startOse) {
414: startOse = null;
415: }
416: if (ose == endOse) {
417: endOse = null;
418: }
419: // Include all elements that are fully encapsulated by the timespan
420: resultElements
421: .add(new ObjectScheduleElement(ose.getStartTime(),
422: ose.getEndTime(), ose.getObject()));
423: }
424: if ((startOse == endOse) && (startOse != null)) {
425: resultElements.add(new ObjectScheduleElement(start, end,
426: startOse.getObject()));
427: } else {
428: if (startOse != null) {
429: // start does not fully encapsulate this element
430: // adjust start of return schedule to reflect time span restriction
431: resultElements.add(new ObjectScheduleElement(start,
432: startOse.getEndTime(), startOse.getObject()));
433: }
434: if (endOse != null) {
435: // end does not fully encapsulate this element
436: // adjust end of return schedule to reflect time span restriction
437: resultElements.add(new ObjectScheduleElement(endOse
438: .getStartTime(), end, endOse.getObject()));
439: }
440: }
441: return newObjectSchedule(resultElements.elements());
442: }
443:
444: // Only works on contiguous object schedules
445: public static Schedule simplifyObjectSchedule(Schedule sched) {
446: if (sched == null) {
447: return null;
448: }
449: if (sched.isEmpty()) {
450: return sched;
451: }
452: Vector newElements = new Vector();
453: ObjectScheduleElement element = null;
454: long start = sched.getStartTime();
455: long elementStart = Long.MIN_VALUE, elementEnd = Long.MAX_VALUE;
456: Object o = null, test = null;
457: while ((element = (ObjectScheduleElement) getElementWithTime(
458: sched, start)) != null) {
459: if (o == null) {
460: o = element.getObject();
461: elementStart = element.getStartTime();
462: elementEnd = element.getEndTime();
463: start = elementEnd;
464: } else {
465: test = element.getObject();
466: if (test.equals(o)) {
467: elementEnd = element.getEndTime();
468: } else {
469: newElements.add(new ObjectScheduleElement(
470: elementStart, elementEnd, o));
471: elementStart = element.getStartTime();
472: elementEnd = element.getEndTime();
473: o = test;
474: }
475: start = element.getEndTime();
476: }
477: }
478: if (o != null) {
479: newElements.add(new ObjectScheduleElement(elementStart,
480: elementEnd, o));
481: }
482: return newObjectSchedule(newElements.elements());
483: }
484: }
|