/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package simulation;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.SwingUtilities;
import ilog.views.maps.IlvGeodeticComputation;
import ilog.views.maps.IlvLinearUnit;
import ilog.views.maps.defense.symbology.app6a.IlvApp6aSymbol;
import ilog.views.sdm.IlvSDMEngine;
/**
* Abstract class for symbols moving on the map.
*
* @see Missile
* @see Target
*
*/
public abstract class Mobile {
static boolean paused = false;
private static final class MobileTask extends TimerTask {
Override
public void run() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
Override
public void run() {
IlvSDMEngine symbology = null;
if (allMobiles.size() > 0) {
Mobile m = (Mobile) allMobiles.get(0);
symbology = m.symbology;
}
if (symbology != null)
symbology.getModel().setAdjusting(true);
for (int i = allMobiles.size() - 1; i >= 0; i--) {
synchronized (allMobiles) {
if (i >= allMobiles.size())
continue;
Mobile m = (Mobile) allMobiles.get(i);
m.updateLocation();
}
}
if (symbology != null)
symbology.getModel().setAdjusting(false);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Prevents changes in location
*/
public void pause() {
paused = true;
}
/**
* Allows changes in location
*/
public void resume() {
paused = false;
}
}
final static Vector<Mobile> allMobiles = new Vector<Mobile>();
/**
* The symbol used to display an icon on the object.
*/
final protected IlvApp6aSymbol symbol;
final IlvSDMEngine symbology;
boolean wrecked = false;
/**
* Returns the symbol used to display this object.
*
* @return the icon of the object
*/
public IlvApp6aSymbol getSymbol() {
return symbol;
}
Mobile(String tag, IlvSDMEngine sym, String id) {
if (tag == null) {
symbol = new IlvApp6aSymbol(id, 0, 0);
} else {
symbol = new IlvApp6aSymbol(tag, id, 0, 0);
}
this.symbology = sym;
sym.getModel().addObject(symbol, null, null);
// if (allMobiles.size() == 0)
// Mobile.start();
// allTimer.start();
allMobiles.add(this);
}
private double horizontalSpeed;
private double verticalSpeed;
private double direction;
private long lastUpdateTime;
long getLastUpdateTime() {
return lastUpdateTime;
}
double getHorizontalSpeed() {
return horizontalSpeed;
}
double getVerticalSpeed() {
return verticalSpeed;
}
double getDirection() {
return direction;
}
void setDirection(double direction) {
this.direction = direction;
}
void setHorizontalSpeed(double horizontalSpeed) {
this.horizontalSpeed = horizontalSpeed;
}
void setVerticalSpeed(double verticalSpeed) {
this.verticalSpeed = verticalSpeed;
}
/**
* Called by the scenario to update the location of the target.
*
* @see Mobile#updateLocation()
*/
public void updateLocation() {
if (isWrecked()) {
return;
}
double olon = symbol.getLongitude();
double olat = symbol.getLatitude();
long updateTime = System.currentTimeMillis();
double dt;
if (!paused) {
if (lastUpdateTime == 0) {
dt = 0;
} else {
dt = (updateTime - lastUpdateTime) / 1000.0;
}
dt *= SimulationContext.getTimeAcceleration();
// distance ran.
double dist = dt * getHorizontalSpeed();
// compute new lat/lon
IlvGeodeticComputation gc = new IlvGeodeticComputation();
gc.setLatitude1(olat);
gc.setLongitude1(olon);
gc.setDistance(dist);
gc.setForwardAzimuth(getDirection());
gc.computeGeodeticForward();
lastUpdateTime = updateTime;
// update location
boolean ia = symbology.getModel().isAdjusting();
if (!ia)
symbology.getModel().setAdjusting(true);
symbol.setLocation(symbology, gc.getLongitude2(), gc.getLatitude2());
// simulate some altitude change
double dh = getVerticalSpeed() * dt;
double altitude = getAltitude();
setSymbolProperty(IlvApp6aSymbol.MODIFIER_ALTITUDE_OR_DEPTH, (int) (altitude + dh) + " ft", false); //$NON-NLS-1$
// as the computing other fields take time, do not update them every cycle
// for all the neutral objects.
if (symbol.getTag() != SimulationController.NEUTRAL.intern()
|| symbol.getProperty(SimulationController.FOLLOW) != null) {
updateOptional();
}
if (!ia)
symbology.getModel().setAdjusting(false);
}
lastUpdateTime = updateTime;
}
void updateOptional() {
// update direction
setSymbolProperty(IlvApp6aSymbol.MODIFIER_DIRECTION_OF_MOVEMENT_INDICATOR,
"" + Math.toDegrees(getDirection() - Math.PI / 2), false); //$NON-NLS-1$
double altitude = getAltitude();
if (altitude < getMinAltitude()) {
setVerticalSpeed(Math.abs(getVerticalSpeed()));
}
if (altitude > getMaxAltitude()) {
setVerticalSpeed(-Math.abs(getVerticalSpeed()));
}
String verticalDir = "\u25B2"; // up arrow //$NON-NLS-1$
if (getVerticalSpeed() < 0)
verticalDir = "\u25BC";// down arrow //$NON-NLS-1$
setSymbolProperty(IlvApp6aSymbol.MODIFIER_LOCATION, verticalDir, false);
double ms = getHorizontalSpeed();
ms = ms * IlvLinearUnit.MI.fromMeters(1) * 3600;
setSymbolProperty(IlvApp6aSymbol.MODIFIER_SPEED, ((int) ms) + " mph", true); //$NON-NLS-1$
}
/**
* Returns the minimum altitude the mobile should reach in the simulation.
*
* @return the minimum altitude (1000 by default)
*/
public double getMinAltitude() {
return 1000;
}
/**
* Returns the maximum altitude the mobile should reach in the simulation.
*
* @return the maximum altitude (3000 by default)
*/
public double getMaxAltitude() {
return 3000;
}
/**
* Called when the target is hit by a missile.
*/
public final void wreck() {
allMobiles.remove(this);
symbology.getModel().removeObject(symbol);
}
/**
* Retrieves the altitude from the
* {@link IlvApp6aSymbol#MODIFIER_ALTITUDE_OR_DEPTH} property of the symbol.
*
* @return the altitude found (or 2000).
*/
public double getAltitude() {
double altitude = 2000;
String sh2 = (String) getSymbol().getProperty(IlvApp6aSymbol.MODIFIER_ALTITUDE_OR_DEPTH);
if (sh2 != null) {
try {
altitude = SimulationController.FS.parse(sh2).doubleValue();
} catch (ParseException e) {// ignore
}
}
return altitude;
}
/**
* Sets a symbol property.
*
* @param propName
* property name.
* @param propValue
* property value.
* @param fireChange
* true if this property change needs to fire the change to the
* engine.
*/
protected void setSymbolProperty(String propName, String propValue, boolean fireChange) {
if (fireChange) {
symbology.getModel().setObjectProperty(symbol, propName, propValue);
} else {
symbol.setProperty(propName, propValue);
}
}
boolean isWrecked() {
return wrecked;
}
static java.util.Timer tt;
static synchronized void stop() {
if (tt != null) {
try {
tt.cancel();
tt = null;
} catch (IllegalStateException e) {
System.err.println("cannot stop " + e);//$NON-NLS-1$
}
}
}
static synchronized void pause() {
if (currentMobileTask != null) {
currentMobileTask.pause();
}
}
static synchronized void resume() {
if (currentMobileTask != null) {
currentMobileTask.resume();
}
}
static synchronized void start() {
stop();
try {
tt = new java.util.Timer();
currentMobileTask = new MobileTask();
tt.scheduleAtFixedRate(currentMobileTask, 1000 / SimulationContext.getTargetFPS(),
1000 / SimulationContext.getTargetFPS());
} catch (IllegalStateException e) {
System.err.println("cannot start " + e); //$NON-NLS-1$
}
}
static MobileTask currentMobileTask;
}