/* * 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.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", Double.valueOf(lon));//$NON-NLS-1$ node.setProperty(/* JGP TEMP IlvMapRenderer.LATITUDE_PROPERTY */"latitude", Double.valueOf(lat));//$NON-NLS-1$ } else { boolean ia = engine.getModel().isAdjusting(); if (!ia) engine.getModel().setAdjusting(true); engine.getModel().setObjectProperty(node, "longitude", Double.valueOf(lon)); //$NON-NLS-1$ engine.getModel().setObjectProperty(node, "latitude", Double.valueOf(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; }