/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © 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; }