/*
* 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.sdm.IlvSDMEngine;
import ilog.views.sdm.model.IlvDefaultSDMNode;
/**
* Abstract class for symbols moving on the map.
*
*/
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 IlvDefaultSDMNode symbol;
final IlvSDMEngine symbology;
boolean wrecked = false;
int status = 0;
/**
* Returns the symbol used to display this object.
*
* @return the icon of the object
*/
public IlvDefaultSDMNode getSymbol() {
return symbol;
}
/**
*
* @param tag
* @param sym
* @param id
*/
Mobile(String tag, IlvSDMEngine sym) {
symbol = new IlvDefaultSDMNode(tag);
lastStatusUpdateTime = System.currentTimeMillis() - 40000;
this.symbology = sym;
symbol.setProperty("status", "" + status); //$NON-NLS-1$ //$NON-NLS-2$
sym.getModel().addObject(symbol, null, null);
allMobiles.add(this);
}
private double horizontalSpeed;
private double verticalSpeed;
private double direction;
/**
*
*/
protected 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;
}
static double getLongitude(IlvDefaultSDMNode node) {
Double lon = (Double) node
.getProperty(/* JGP TEMP IlvMapRenderer.LONGITUDE_PROPERTY */"longitude");//$NON-NLS-1$
return lon.doubleValue();
}
static double getLatitude(IlvDefaultSDMNode node) {
Double lat = (Double) node
.getProperty(/* JGP TEMP IlvMapRenderer.LATITUDE_PROPERTY */"latitude");//$NON-NLS-1$
return lat.doubleValue();
}
/**
*
* @param node
* @param engine
* @param lon
* @param lat
*/
protected void setLocation(IlvDefaultSDMNode node, IlvSDMEngine engine, double lon, double lat) {
if (engine == null) {
node.setProperty(/* JGP TEMP IlvMapRenderer.LONGITUDE_PROPERTY */"longitude", new Double(lon));//$NON-NLS-1$
node.setProperty(/* JGP TEMP IlvMapRenderer.LATITUDE_PROPERTY */"latitude", new Double(lat));//$NON-NLS-1$
} else {
boolean ia = engine.getModel().isAdjusting();
if (!ia)
engine.getModel().setAdjusting(true);
engine.getModel().setObjectProperty(node, "longitude", new Double(lon)); //$NON-NLS-1$
engine.getModel().setObjectProperty(node, "latitude", new Double(lat));//$NON-NLS-1$
if (!ia)
engine.getModel().setAdjusting(false);
}
}
private long lastStatusUpdateTime;
private long timeWhenPaused = -1;
private long timeWhenResumed = -1;
/**
*
*/
protected void updateStatus() {
long currentTime = System.currentTimeMillis();
if (paused) {
if (timeWhenPaused == -1) {
timeWhenPaused = currentTime;
timeWhenResumed = -1;
}
} else {
if (timeWhenResumed == -1) {
timeWhenResumed = currentTime;
lastStatusUpdateTime += (timeWhenResumed - timeWhenPaused);
timeWhenPaused = -1;
}
}
if (!paused) {
long diff = currentTime - lastStatusUpdateTime;
double timeDifference = diff * SimulationContext.getTimeAcceleration();
double decision = timeDifference / (timeDifference + 20000 * (Math.random() + 0.5));
if (decision > 0.8) {
// change status
decision = Math.random();
int newStatus = 0; // (might remain the same)
if (decision > 0.8) {
newStatus = 2;
} else if (decision > 0.5) {
newStatus = 1;
}
if (newStatus != status) {
setSymbolProperty("status", "" + newStatus, true); //$NON-NLS-1$ //$NON-NLS-2$
status = newStatus;
lastStatusUpdateTime = currentTime;
}
}
}
}
/**
* Called by the scenario to update the location of the target.
*
* @see Mobile#updateLocation()
*/
public void updateLocation() {
if (isWrecked()) {
return;
}
double olon = getLongitude(symbol);
double olat = getLatitude(symbol);
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);
setLocation(symbol, symbology, gc.getLongitude2(), gc.getLatitude2());
// simulate some altitude change
double dh = getVerticalSpeed() * dt;
double altitude = getAltitude();
setSymbolProperty(MODIFIER_ALTITUDE_OR_DEPTH, (int) (altitude + dh) + " ft", false); //$NON-NLS-1$
setSymbolProperty(MODIFIER_DIRECTION_OF_MOVEMENT_INDICATOR, "" + Math.toDegrees(getDirection() - Math.PI / 2), //$NON-NLS-1$
false);
if (!ia) {
symbology.getModel().setAdjusting(false);
}
}
lastUpdateTime = updateTime;
updateStatus();
}
/**
*
*/
protected static final String MODIFIER_ALTITUDE_OR_DEPTH = "ALTITUDE_OR_DEPTH";//$NON-NLS-1$
/**
*
*/
protected static final String MODIFIER_DIRECTION_OF_MOVEMENT_INDICATOR = "DIRECTION_OF_MOVEMENT_INDICATOR";//$NON-NLS-1$
/**
*
*/
protected static final String MODIFIER_LOCATION = "LOCATION";//$NON-NLS-1$
/**
*
*/
protected static final String MODIFIER_SPEED = "SPEED";//$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 #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(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;
}