AnimatorInterface.java

package nl.tudelft.simulation.dsol.simulators;

import java.rmi.RemoteException;

import org.djutils.event.EventType;
import org.djutils.metadata.MetaData;
import org.djutils.metadata.ObjectDescriptor;

/**
 * The AnimatorInterface defines the methods for a DEVSDESS simulator with wallclock delay between the consecutive time steps.
 * <p>
 * Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
 * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
 * project is distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
 * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
 * </p>
 * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 */
public interface AnimatorInterface
{
    /** DEFAULT_ANIMATION_DELAY of 100 milliseconds used in the animator. */
    long DEFAULT_ANIMATION_DELAY = 100L;

    /** UPDATE_ANIMATION_EVENT is fired to wake up animatable components. */
    EventType UPDATE_ANIMATION_EVENT = new EventType(new MetaData("UPDATE_ANIMATION_EVENT", "Animation update"));

    /** ANIMATION_DELAY_CHANGED_EVENT is fired when the time step is set. */
    EventType ANIMATION_DELAY_CHANGED_EVENT = new EventType(new MetaData("ANIMATION_DELAY_CHANGED_EVENT",
            "Animation delay changed", new ObjectDescriptor("newDelay", "new animation delay", Long.class)));

    /**
     * returns the animation delay in milliseconds between each consecutive animation update.
     * @return long; the animation delay in milliseconds of wallclock time
     * @throws RemoteException on network failure
     */
    long getAnimationDelay() throws RemoteException;

    /**
     * sets the animationDelay using wallclock time in milliseconds.
     * @param milliseconds long; the animation delay in milliseconds
     * @throws RemoteException on network failure
     */
    void setAnimationDelay(long milliseconds) throws RemoteException;

    /**
     * updateAnimation takes care of firing the UPDATE_ANIMATION_EVENT.
     */
    void updateAnimation();

    /**
     * The separate thread that takes care of the animation.
     * <p>
     * Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
     * See for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>.
     * The DSOL project is distributed under a three-clause BSD-style license, which can be found at
     * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
     * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
     * </p>
     * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
     */
    class AnimationThread extends Thread
    {
        /** is the animator running? */
        private boolean running = true;

        /** the animator. */
        private final AnimatorInterface animator;

        /**
         * @param animator AnimatorInterface; the animator.
         */
        public AnimationThread(final AnimatorInterface animator)
        {
            super();
            this.animator = animator;
        }

        /** {@inheritDoc} */
        @Override
        public void run()
        {
            long lastTime = System.nanoTime();
            while (this.running)
            {
                try
                {
                    long delta = System.nanoTime() - lastTime;
                    sleep(Math.max(1, this.animator.getAnimationDelay() - delta / 1000000));
                    lastTime = System.nanoTime();
                    this.animator.updateAnimation();
                }
                catch (InterruptedException exception)
                {
                    // if interrupted by stopAnimation, this.running is false and the animation stops.
                    this.running = false;
                }
                catch (RemoteException exception)
                {
                    this.running = false;
                }
            }
        }

        /**
         * Stop the animation.
         */
        public void stopAnimation()
        {
            this.running = false;
            interrupt();
        }
    }
}