edu.mit.fss.DefaultFederate.java Source code

Java tutorial

Introduction

Here is the source code for edu.mit.fss.DefaultFederate.java

Source

/*
 * Copyright 2015 Paul T. Grogan
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package edu.mit.fss;

import hla.rti1516e.exceptions.RTIexception;
import hla.rti1516e.exceptions.RTIinternalError;

import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.event.EventListenerList;

import org.apache.commons.math3.util.FastMath;
import org.apache.log4j.Logger;

import edu.mit.fss.event.ExecutionControlEvent;
import edu.mit.fss.event.ExecutionControlListener;
import edu.mit.fss.event.ObjectChangeListener;
import edu.mit.fss.event.SimulationTimeEvent;
import edu.mit.fss.event.SimulationTimeListener;
import edu.mit.fss.hla.FSSambassador;
import edu.mit.fss.hla.DefaultAmbassador;
import edu.mit.fss.hla.FederationConnection;

/**
 * A default implementation of the {@link Federate} interface. Sets a default
 * initial time of 0 (corresponding to January 1, 1970), a default time step
 * of 1 minute, and a default minimum step duration of 100 milliseconds.
 * <p>
 * Provides thread-safe methods for {@link #addObject(SimObject)}, 
 * {@link #removeObject(SimObject)}, {@link #connect()}, 
 * {@link #initialize()}, {@link #run()}, {@link #tickTock()}, 
 * {@link #stop()}, {@link #terminate()}, {@link #disconnect()}, and 
 * {@link #exit()}.
 * 
 * @author Paul T. Grogan, ptgrogan@mit.edu
 * @version 0.2.0
 * @since 0.1.0
 */
public class DefaultFederate implements Federate {
    private static Logger logger = Logger.getLogger(DefaultFederate.class);
    private final FSSambassador ambassador;
    private final EventListenerList listenerList = new EventListenerList();
    private final Set<SimObject> localObjects = Collections.synchronizedSet(new HashSet<SimObject>());
    private long initialTime, finalTime, timeStep;
    private long minimumStepDuration, lookahead;
    private long time;
    private AtomicBoolean initialized = new AtomicBoolean(false);
    private volatile AtomicBoolean running = new AtomicBoolean(false);
    private volatile AtomicBoolean stopping = new AtomicBoolean(false);
    private volatile AtomicBoolean terminating = new AtomicBoolean(false);
    private volatile long nextTimeStep, nextMinimumStepDuration, nextFinalTime;

    /**
     * Instantiates a new default federate using a {@link DefaultAmbassador} 
     * federate ambassador implementation.
     * 
     * @throws RTIinternalError 
     */
    public DefaultFederate() throws RTIexception {
        this(new DefaultAmbassador());
    }

    /**
     * Instantiates a new default federate using the provided federate 
     * ambassador {@link ambassador}. Assigns a default initial time of 0, a
     * final time of Long.MAX_VALUE, a time step of 60000 ms, a minimum
     * step duration of 100 ms, and a lookahead of 1000 ms.
     *
     * @param ambassador the ambassador
     */
    public DefaultFederate(FSSambassador ambassador) {
        this.ambassador = ambassador;
        initialTime = 0;
        finalTime = Long.MAX_VALUE;
        timeStep = 60 * 1000;
        minimumStepDuration = 100;
        lookahead = 1000;
        nextTimeStep = timeStep;
        nextMinimumStepDuration = minimumStepDuration;
        nextFinalTime = finalTime;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#addExecutionControlListener(edu.mit.fss.gui.ExecutionControlListener)
     */
    @Override
    public void addExecutionControlListener(ExecutionControlListener listener) {
        listenerList.add(ExecutionControlListener.class, listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#addObject(edu.mit.fss.SimObject)
     */
    @Override
    public synchronized void addObject(SimObject object) {
        if (initialized.get()) {
            // thread safe initialization as tickTock is synchronized
            object.initialize(time);
            // add object to federation if simulation is initialized
            ambassador.scheduleObjectCreation(object);
        }
        // add objec to local simulation objects
        localObjects.add(object);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#addObjectChangeListener(edu.mit.fss.gui.ObjectChangeListener)
     */
    @Override
    public void addObjectChangeListener(ObjectChangeListener listener) {
        ambassador.addObjectChangeListener(listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#addSimulationTimeListener(edu.mit.fss.gui.SimulationTimeListener)
     */
    @Override
    public void addSimulationTimeListener(SimulationTimeListener listener) {
        listenerList.add(SimulationTimeListener.class, listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#connect()
     */
    @Override
    public void connect() {
        ambassador.connect();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#disconnect()
     */
    @Override
    public void disconnect() {
        // terminate before disconnecting
        terminate();
        ambassador.disconnect();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#exit()
     */
    @Override
    public void exit() {
        // terminate and disconnect before exiting
        terminate();
        disconnect();
        System.exit(0);
    }

    /**
     * Fires an execution initialized event.
     */
    protected final void fireExecutionInitializedEvent() {
        ExecutionControlListener[] listeners = listenerList.getListeners(ExecutionControlListener.class);
        for (int i = 0; i < listeners.length; i++) {
            listeners[i].executionInitialized(new ExecutionControlEvent(this));
        }
    }

    /**
     * Fires an execution started event.
     */
    protected final void fireExecutionStartedEvent() {
        ExecutionControlListener[] listeners = listenerList.getListeners(ExecutionControlListener.class);
        for (int i = 0; i < listeners.length; i++) {
            listeners[i].executionStarted(new ExecutionControlEvent(this));
        }
    }

    /**
     * Fires an execution stopped event.
     */
    protected final void fireExecutionStoppedEvent() {
        ExecutionControlListener[] listeners = listenerList.getListeners(ExecutionControlListener.class);
        for (int i = 0; i < listeners.length; i++) {
            listeners[i].executionStopped(new ExecutionControlEvent(this));
        }
    }

    /**
     * Fires an execution terminated event.
     */
    protected final void fireExecutionTerminatedEvent() {
        ExecutionControlListener[] listeners = listenerList.getListeners(ExecutionControlListener.class);
        for (int i = 0; i < listeners.length; i++) {
            listeners[i].executionTerminated(new ExecutionControlEvent(this));
        }
    }

    /**
     * Fires an simulation time advanced event.
     */
    protected final void fireSimulationTimeAdvancedEvent() {
        SimulationTimeListener[] listeners = listenerList.getListeners(SimulationTimeListener.class);
        for (int i = 0; i < listeners.length; i++) {
            listeners[i].timeAdvanced(new SimulationTimeEvent(this, time));
        }
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#getConnection()
     */
    @Override
    public FederationConnection getConnection() {
        return ambassador.getConnection();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#getFinalTime()
     */
    @Override
    public long getFinalTime() {
        return finalTime;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#getInitialTime()
     */
    @Override
    public long getInitialTime() {
        return initialTime;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#getMinimumStepDuration()
     */
    @Override
    public long getMinimumStepDuration() {
        return minimumStepDuration;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#getTimeStep()
     */
    @Override
    public long getTimeStep() {
        return timeStep;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#initialize()
     */
    @Override
    public synchronized void initialize() {
        // update initial time -- federation may be in the future
        time = ambassador.initialize(initialTime, lookahead);

        synchronized (localObjects) {
            for (SimObject object : localObjects) {
                object.initialize(time);
                ambassador.scheduleObjectCreation(object);
            }
        }

        initialized.set(true);
        fireExecutionInitializedEvent();
        fireSimulationTimeAdvancedEvent();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#removeExecutionControlListener(edu.mit.fss.gui.ExecutionControlListener)
     */
    @Override
    public void removeExecutionControlListener(ExecutionControlListener listener) {
        listenerList.remove(ExecutionControlListener.class, listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#removeObject(edu.mit.fss.SimObject)
     */
    @Override
    public synchronized void removeObject(SimObject object) {
        if (initialized.get()) {
            // delete object from federation if simulation is initialized
            ambassador.scheduleObjectDeletion(object);
        }
        // remove from local simulation objects
        localObjects.remove(object);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#removeObjectChangeListener(edu.mit.fss.ObjectChangeListener)
     */
    @Override
    public void removeObjectChangeListener(ObjectChangeListener listener) {
        ambassador.removeObjectChangeListener(listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#removeSimulationTimeListneer(edu.mit.fss.gui.SimulationTimeListener)
     */
    @Override
    public void removeSimulationTimeListener(SimulationTimeListener listener) {
        listenerList.remove(SimulationTimeListener.class, listener);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#run(long)
     */
    @Override
    public void run() {
        if (!initialized.get()) {
            throw new IllegalStateException("Simulation is not initialized.");
        }
        logger.trace("Running the federation.");
        running.set(true);
        fireExecutionStartedEvent();
        while (time < getFinalTime() && !stopping.get() && !terminating.get()) {
            tickTock();
        }
        logger.trace("Exiting the running loop.");
        if (stopping.get()) {
            stopping.set(false);
            logger.trace("Federation has stopped.");
        }
        if (terminating.get()) {
            terminating.set(false);
            logger.trace("Federation has terminated.");
        }
        running.set(false);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#sendInteraction(edu.mit.fss.SimInteraction)
     */
    @Override
    public void sendInteraction(SimInteraction interaction) {
        if (!initialized.get()) {
            throw new IllegalStateException("Simulation is not initialized.");
        }

        ambassador.scheduleInteraction(interaction);
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#setFinalTime(long)
     */
    @Override
    public void setFinalTime(long finalTime) {
        logger.trace("Setting next final time to " + finalTime + " (" + new Date(finalTime) + ").");
        nextFinalTime = finalTime;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#setInitialTime(long)
     */
    @Override
    public void setInitialTime(long initialTime) {
        logger.trace("Setting initial time to " + initialTime + " (" + new Date(initialTime) + ").");
        this.initialTime = initialTime;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#setMinimumStepDuration(long)
     */
    @Override
    public void setMinimumStepDuration(long minimumStepDuration) {
        logger.trace("Setting next minimum step duration to " + minimumStepDuration + " ms.");
        nextMinimumStepDuration = minimumStepDuration;
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#setTimeStep(long)
     */
    @Override
    public void setTimeStep(long timeStep) {
        if (timeStep < lookahead) {
            logger.warn("Time step (" + timeStep + " ms) cannot be smaller than lookahead (" + lookahead
                    + " ms), setting to lookahead.");
            nextTimeStep = lookahead;
        } else {
            logger.trace("Setting next time step to " + timeStep + " ms.");
            nextTimeStep = timeStep;
        }
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#stop()
     */
    @Override
    public void stop() {
        logger.trace("Stopping the federation...");
        if (running.get()) {
            stopping.set(true);
            // wait until execution is stopped
            while (stopping.get()) {
                Thread.yield();
            }
        }
        fireExecutionStoppedEvent();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.gui.Federate#terminate()
     */
    @Override
    public void terminate() {
        logger.trace("Terminating the federate.");
        if (running.get()) {
            terminating.set(true);
            // wait until execution is terminated
            while (terminating.get()) {
                Thread.yield();
            }
        }
        if (initialized.get()) {
            initialized.set(false);
            // delete all simulation objects from federation
            synchronized (localObjects) {
                for (SimObject object : localObjects) {
                    ambassador.scheduleObjectDeletion(object);
                }
            }
        }
        ambassador.terminate();
        fireExecutionTerminatedEvent();
    }

    /* (non-Javadoc)
     * @see edu.mit.fss.Federate#tickTock()
     */
    @Override
    public synchronized void tickTock() {
        if (!initialized.get()) {
            throw new IllegalStateException("Simulation is not initialized.");
        }
        logger.trace("Tick-tocking federation.");

        // log the wallclock time
        long systemTime = new Date().getTime();

        timeStep = nextTimeStep;
        minimumStepDuration = nextMinimumStepDuration;
        finalTime = nextFinalTime;

        synchronized (localObjects) {
            logger.trace("Ticking all federate objects.");
            for (SimObject object : localObjects) {
                object.tick(timeStep);
            }
            logger.trace("Tocking all federate objects.");
            for (SimObject object : localObjects) {
                object.tock();
                ambassador.scheduleObjectUpdate(object);
            }
        }
        logger.trace("Advancing simulation time.");
        time += timeStep;
        ambassador.advanceTo(time);
        fireSimulationTimeAdvancedEvent();

        try {
            logger.trace("Waiting for minimum step duration.");
            Thread.sleep(FastMath.max(0, minimumStepDuration - (new Date().getTime() - systemTime)));
        } catch (InterruptedException ignored) {
        }
    }
}