001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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.trans;
027:
028: import org.cougaar.glm.ldm.asset.Container;
029: import org.cougaar.glm.ldm.asset.GLMAsset;
030: import org.cougaar.glm.ldm.asset.Organization;
031: import org.cougaar.glm.util.GLMMeasure;
032: import org.cougaar.glm.util.GLMPreference;
033: import org.cougaar.glm.util.GLMPrepPhrase;
034: import org.cougaar.lib.util.UTILAsset;
035: import org.cougaar.lib.vishnu.client.custom.CustomDataXMLize;
036: import org.cougaar.logistics.ldm.Constants;
037: import org.cougaar.planning.ldm.asset.AggregateAsset;
038: import org.cougaar.planning.ldm.asset.Asset;
039: import org.cougaar.planning.ldm.asset.AssetGroup;
040: import org.cougaar.planning.ldm.measure.Distance;
041: import org.cougaar.planning.ldm.measure.Latitude;
042: import org.cougaar.planning.ldm.measure.Longitude;
043: import org.cougaar.planning.ldm.plan.LatLonPoint;
044: import org.cougaar.planning.ldm.plan.NamedPosition;
045: import org.cougaar.planning.ldm.plan.Relationship;
046: import org.cougaar.planning.ldm.plan.Role;
047: import org.cougaar.planning.ldm.plan.Task;
048: import org.cougaar.util.TimeSpan;
049: import org.cougaar.util.log.Logger;
050: import java.util.Collection;
051: import java.util.Date;
052: import java.util.Iterator;
053: import java.util.Set;
054:
055: /**
056: * Create either an XML document in the Vishnu Data format or Vishnu objects from ALP objects. <p>
057: * <p>
058: * Overrides processAsset to exclude extraneous assets and processTask to add fields.
059: * <p>
060: */
061: public class TranscomDataXMLize extends CustomDataXMLize {
062: public static final String TRUE = "true";
063: public static final String FALSE = "false";
064:
065: protected Set assetIncludeSet;
066: protected UTILAsset assetHelper;
067: protected Organization self;
068:
069: private static double EARTH_RADIUS = 3437.75d; // nmi
070: private static double DEGREES_TO_RADIANS = (3.1415927d / 180.0d);
071:
072: public TranscomDataXMLize(boolean direct, Logger logger,
073: Set includeSet) {
074: super (direct, logger);
075: glmPrepHelper = new GLMPrepPhrase(logger);
076: glmPrefHelper = new GLMPreference(logger);
077: measureHelper = new GLMMeasure(logger);
078:
079: this .assetIncludeSet = includeSet;
080: this .assetHelper = new UTILAsset(logger);
081: }
082:
083: public boolean interestingTask(Task t) {
084: boolean hasTransportVerb = t.getVerb().equals(
085: Constants.Verb.TRANSPORT);
086:
087: return hasTransportVerb;
088: }
089:
090: /**
091: * Create XML for asset, subclass to add fields <p>
092: *
093: * Ignore everything but GlobalAir, GlobalSea, and the null asset.
094: *
095: * Uses statics for asset names from TranscomVishnuPlugin.
096: * These are settable by properties.
097: *
098: * NOTE : field names should match those in .dff file
099: *
100: * @param object node representing asset
101: * @param taskOrAsset asset being translated
102: * @return true if should add object to list of new objects
103: */
104: protected boolean processAsset(Object object, Object taskOrAsset) {
105: setType(object, "Asset");
106: Asset asset = (Asset) taskOrAsset;
107: String type;
108:
109: if (asset instanceof Organization) {
110: type = getOrganizationRole(asset);
111: String name = getAssetName(asset);
112: String parentType = "Asset";
113:
114: dataHelper.createField(object, parentType, "id", asset
115: .getUID().toString());
116: dataHelper.createField(object, parentType, "type", type);
117: dataHelper.createField(object, parentType, "name", name);
118:
119: if (logger.isDebugEnabled())
120: logger.debug("TranscomDataXMLize.processAsset - asset "
121: + asset.getUID() + " type " + type + " name "
122: + name);
123: } else
124: type = setName("Asset", object, asset);
125:
126: if (direct && logger.isDebugEnabled())
127: logger
128: .debug("TranscomDataXMLize.processAsset - created resource "
129: + object);
130:
131: // ignore all but global air, sea, and nomenclature
132: return (assetIncludeSet.contains(type));
133: }
134:
135: /**
136: * Get the relationship role of the org, where we're only really interested
137: * in GlobalSea (SeaTransportationProvider) and GlobalAir (AirTransportationProvider)
138: */
139: protected String getOrganizationRole(Asset asset) {
140: Organization org = (Organization) asset;
141: if (org.isSelf())
142: self = org;
143:
144: Collection providers = org.getRelationshipSchedule()
145: .getMatchingRelationships(
146: Constants.RelationshipType.PROVIDER_SUFFIX,
147: TimeSpan.MIN_VALUE, TimeSpan.MAX_VALUE);
148:
149: if (providers == null || providers.isEmpty()) {
150: if (logger.isInfoEnabled()) {
151: logger
152: .info("TranscomDataXMLize.getOrganizationRole - no providers for "
153: + org
154: + " schedule is "
155: + org.getRelationshipSchedule()
156: + " so getting superiors (which should be a TRANSCOM agent).");
157: }
158:
159: /*
160: if (self != null) {
161: RelationshipSchedule schedule = self.getRelationshipSchedule();
162:
163: Collection selfProviders =
164: schedule.getMatchingRelationships(Constants.RelationshipType.PROVIDER_SUFFIX,
165: TimeSpan.MIN_VALUE,
166: TimeSpan.MAX_VALUE);
167: if (selfProviders.isEmpty()) {
168: if (logger.isInfoEnabled()) {
169: logger.info ("hmmm, no providers on self schedule, it's " + schedule);
170: }
171: }
172:
173: for (Iterator iter = selfProviders.iterator (); iter.hasNext(); ) {
174: Relationship relation = (Relationship) iter.next();
175: if (schedule.getOther (relation).equals (org)) {
176: Role subRoleA = relation.getRoleA();
177: Role subRoleB = relation.getRoleB();
178: if (logger.isWarnEnabled()) {
179: logger.warn ("TranscomDataXMLize.getOrganizationRole - returning role for " + org);
180: }
181: return (subRoleA.getName().startsWith ("Converse") ? subRoleB.getName () : subRoleA.getName());
182: }
183: else {
184: if (logger.isInfoEnabled()) {
185: logger.info ("TranscomDataXMLize.getOrganizationRole - skipping provider " + schedule.getOther(relation) +
186: " that is not new org " + org);
187: }
188: }
189: }
190: }
191: else {
192: if (logger.isInfoEnabled()) {
193: logger.info ("TranscomDataXMLize.getOrganizationRole - no self org.");
194: }
195: }
196:
197: providers = org.getSuperiors(TimeSpan.MIN_VALUE, TimeSpan.MAX_VALUE);
198: */
199: }
200:
201: if (providers == null)
202: return "NO_PROVIDERS";
203:
204: if (providers.size() > 1) {
205: if (logger.isInfoEnabled()) {
206: logger
207: .info("TranscomDataXMLize.getOrganizationRole - NOTE - org "
208: + org + " has multiple providers :");
209: if (logger.isInfoEnabled()) {
210: for (Iterator iter = providers.iterator(); iter
211: .hasNext();) {
212: logger.info("\tprovider " + iter.next());
213: }
214: }
215: }
216: return "MANY_PROVIDERS";
217: }
218:
219: if (providers.isEmpty()) {
220: if (logger.isInfoEnabled()) {
221: logger
222: .info("TranscomDataXMLize.getOrganizationRole - Note - no providers for "
223: + org
224: + " schedule is "
225: + org.getRelationshipSchedule());
226: }
227:
228: return "NO_PROVIDERS";
229: }
230:
231: if (providers.size() == 1)
232: if (logger.isInfoEnabled())
233: logger.info("found provider for " + org
234: + " schedule was "
235: + org.getRelationshipSchedule());
236:
237: Relationship relationToSuperior = (Relationship) providers
238: .iterator().next();
239: Role subRoleA = relationToSuperior.getRoleA();
240: Role subRoleB = relationToSuperior.getRoleB();
241: if (logger.isDebugEnabled())
242: logger
243: .debug("TranscomDataXMLize.getOrganizationRole - org "
244: + org
245: + " - roleA "
246: + subRoleA
247: + " roleB "
248: + subRoleB);
249: // don't want the converse one - it seems random which org gets to be A and which B
250: return (subRoleA.getName().startsWith("Converse") ? subRoleB
251: .getName() : subRoleA.getName());
252: }
253:
254: /**
255: * Create XML for task, subclass to add fields <p>
256: *
257: * Adds departure, arrival, from, to, name, type, and isPerson fields.
258: *
259: * NOTE : field names should match those in .dff file
260: *
261: * @param object node representing task
262: * @param taskOrAsset task being translated
263: * @return true if should add object to list of new objects
264: */
265: protected boolean processTask(Object object, Object taskOrAsset) {
266: super .processTask(object, taskOrAsset);
267: Task task = (Task) taskOrAsset;
268: String taskName = getTaskName();
269:
270: // if (logger.isDebugEnabled())
271: // logger.debug ("TranscomDataXMLize.processTask - uid " + task.getUID());
272:
273: dataHelper.createDateField(object, "departure", glmPrefHelper
274: .getReadyAt(task));
275:
276: Date arrival = new Date(glmPrefHelper.getBestDate(task)
277: .getTime());
278: dataHelper.createDateField(object, "arrival", arrival);
279: NamedPosition from = getFromLocation(task);
280: NamedPosition to = getToLocation(task);
281:
282: try {
283: if (direct) {
284: dataHelper.createGeoloc(object, "from", from);
285: dataHelper.createGeoloc(object, "to", to);
286: } else {
287: Object field = dataHelper.createField(object, taskName,
288: "from");
289: dataHelper.createGeoloc(field, "from", from);
290: field = dataHelper.createField(object, taskName, "to");
291: dataHelper.createGeoloc(field, "to", to);
292: }
293: } catch (Exception e) {
294: logger.error("TranscomDataXMLize.createDoc - ERROR - "
295: + " no from or to geoloc on " + task);
296: }
297:
298: try {
299: dataHelper.createField(object, taskName, "name",
300: getAssetName(task.getDirectObject()));
301: dataHelper.createField(object, taskName, "type",
302: getAssetType(task.getDirectObject()));
303: } catch (Exception e) {
304: logger.error("TranscomDataXMLize.createDoc - ERROR - "
305: + " no type id pg on direct object of " + task);
306: }
307:
308: GLMAsset baseAsset = null;
309: Asset directObject = task.getDirectObject();
310:
311: if (directObject instanceof AggregateAsset) {
312: baseAsset = (GLMAsset) ((AggregateAsset) directObject)
313: .getAsset();
314: } else {
315: try {
316: if (task.getDirectObject() instanceof AssetGroup) {
317: if (logger.isInfoEnabled())
318: logger
319: .info("processTasks - got asset group for task "
320: + task.getUID());
321: baseAsset = getBaseAsset((AssetGroup) task
322: .getDirectObject());
323: } else {
324: baseAsset = (GLMAsset) directObject;
325: }
326: } catch (ClassCastException cce) {
327: logger
328: .error("TranscomDataXMLize.processTask - ERROR for task "
329: + task
330: + "\nDirectObject was not a GLMAsset, as expected.");
331: }
332: }
333:
334: addTaskPersonField(object, baseAsset);
335: if (object == null)
336: logger
337: .error("huh? object is null for task "
338: + task.getUID()
339: + " verb "
340: + task.getVerb()
341: + " is there a field missing for the task in the format file?");
342: addIsAmmoField(object, baseAsset);
343:
344: float distance = (float) ((glmPrepHelper.hasPrepNamed(task,
345: GLMTransConst.SEAROUTE_DISTANCE)) ? ((Distance) glmPrepHelper
346: .getIndirectObject(task,
347: GLMTransConst.SEAROUTE_DISTANCE))
348: .getNauticalMiles()
349: : distanceBetween(from, to).getNauticalMiles());
350:
351: dataHelper.createFloatField(object, "distance", distance);
352:
353: if (logger.isDebugEnabled())
354: logger.debug("TranscomDataXMLize.processTask - did task "
355: + task.getUID());
356:
357: if (direct && logger.isDebugEnabled())
358: logger
359: .debug("TranscomDataXMLize.processTask - created task "
360: + object);
361:
362: return true;
363: }
364:
365: private NamedPosition getFromLocation(Task t) {
366: return (NamedPosition) glmPrepHelper.getIndirectObject(t,
367: org.cougaar.glm.ldm.Constants.Preposition.FROM);
368: }
369:
370: private NamedPosition getToLocation(Task t) {
371: return (NamedPosition) glmPrepHelper.getIndirectObject(t,
372: org.cougaar.glm.ldm.Constants.Preposition.TO);
373: }
374:
375: /**
376: * Utility function to calculate the distance between two locations
377: * @param start GeolocLocation starting point
378: * @param end GeolocLocation ending point
379: * @return Distance between the two points
380: */
381: public Distance distanceBetween(LatLonPoint start, LatLonPoint end) {
382: return distanceBetween(start, end, 1.0);
383: }
384:
385: /**
386: * Utility function to calculate the distance between two locations
387: * @param start GeolocLocation starting point
388: * @param end GeolocLocation ending point
389: * @param multiplier Multiplier for the final dist result
390: * @return Distance between the two points
391: */
392: public Distance distanceBetween(LatLonPoint start, LatLonPoint end,
393: double multiplier) {
394: // get Long/Lat
395: Longitude startlong = start.getLongitude();
396: Latitude startlat = start.getLatitude();
397: Longitude endlong = end.getLongitude();
398: Latitude endlat = end.getLatitude();
399:
400: // get radian measures
401: double startlongrad = (startlong.getDegrees() * DEGREES_TO_RADIANS);
402: double startlatrad = (startlat.getDegrees() * DEGREES_TO_RADIANS);
403: double endlongrad = (endlong.getDegrees() * DEGREES_TO_RADIANS);
404: double endlatrad = (endlat.getDegrees() * DEGREES_TO_RADIANS);
405:
406: // calculate distance
407: double deltalong = startlongrad - endlongrad;
408: double distinradians = Math.acos(Math.sin(startlatrad)
409: * Math.sin(endlatrad) + Math.cos(startlatrad)
410: * Math.cos(endlatrad) * Math.cos(deltalong));
411: double retval = EARTH_RADIUS * distinradians;
412:
413: return Distance.newNauticalMiles(retval * multiplier);
414: }
415:
416: /**
417: * try to determine base asset of asset group
418: * Basically, we're trying to determine whether this is ammo or not, or people or not
419: * and we need to go down the nested sets of things until we find atomic assets.
420: */
421: protected GLMAsset getBaseAsset(AssetGroup directObject) {
422: Collection assets = assetHelper.expandAssetGroup(directObject);
423: GLMAsset base = null;
424: for (Iterator iter = assets.iterator(); iter.hasNext()
425: && base == null;) {
426: Asset subasset = (Asset) iter.next();
427: if (subasset instanceof AssetGroup) {
428: base = getBaseAsset((AssetGroup) subasset); // recurse
429: } else if (subasset instanceof AggregateAsset) {
430: base = (GLMAsset) ((AggregateAsset) subasset)
431: .getAsset();
432: } else if (subasset instanceof GLMAsset) {
433: base = (GLMAsset) subasset;
434: } else
435: logger.warn("huh? unknown asset class "
436: + subasset.getClass() + " for asset "
437: + subasset);
438: }
439:
440: if (!(base instanceof GLMAsset))
441: logger.error("huh? " + base + " is not a GLMAsset");
442:
443: return base;
444: }
445:
446: /** subclass if people aren't relevant */
447: protected void addTaskPersonField(Object object, GLMAsset baseAsset) {
448: dataHelper.createBooleanField(object, "isPerson",
449: isPerson(baseAsset));
450: }
451:
452: protected boolean isPerson(GLMAsset asset) {
453: return asset.hasPersonPG();
454: }
455:
456: protected void addIsAmmoField(Object object, GLMAsset baseAsset) {
457: if (object == null)
458: logger.error("huh? object is null testing " + baseAsset);
459:
460: boolean isContainer = isContainer(baseAsset);
461: dataHelper.createField(object, "Transport", "isAmmo",
462: (isContainer ? (isAmmo(baseAsset) ? TRUE : FALSE)
463: : FALSE));
464: }
465:
466: protected boolean isContainer(GLMAsset asset) {
467: LowFidelityAssetPG currentLowFiAssetPG = (LowFidelityAssetPG) asset
468: .resolvePG(LowFidelityAssetPG.class);
469:
470: if (currentLowFiAssetPG != null) {
471: return currentLowFiAssetPG.getCCCDim().getIsContainer();
472: } else {
473: return asset instanceof Container;
474: }
475: }
476:
477: /**
478: * An asset is an ammo container if it has a contents pg, since
479: * only the Ammo Packer put a contents pg on a container.
480: *
481: * NOTE : should call isContainer first!
482: */
483: protected boolean isAmmo(GLMAsset asset) {
484: return asset.hasContentsPG();
485: }
486:
487: protected GLMPrepPhrase glmPrepHelper;
488: protected GLMPreference glmPrefHelper;
489: protected GLMMeasure measureHelper;
490: }
|