SimulatorStateTransceiver.java

package org.opentrafficsim.sim0mq.publisher;

import java.io.Serializable;
import java.rmi.RemoteException;

import org.djutils.event.Event;
import org.djutils.event.EventListener;
import org.djutils.event.EventProducer;
import org.djutils.event.EventType;
import org.djutils.event.LocalEventProducer;
import org.djutils.event.TimedEvent;
import org.djutils.metadata.MetaData;
import org.djutils.metadata.ObjectDescriptor;
import org.djutils.serialization.SerializationException;
import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
import org.sim0mq.Sim0MQException;

import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;

/**
 * Transceiver for simulator state change events.
 * <p>
 * Copyright (c) 2020-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
 */
public class SimulatorStateTransceiver extends AbstractTransceiver
{
    /** The simulator. */
    private final OtsSimulatorInterface simulator;

    /** Multiplexes SimulatorInterface.START_EVENT and SimulatorInterface.STOP_EVENT. */
    @SuppressWarnings("checkstyle:visibilitymodifier")
    final EventProducer eventMultiplexer;

    /** The event that will be emitted for either the START_EVENT or the STOP_EVENT. */
    public static final EventType SIMULATOR_STATE_CHANGED = new EventType(new MetaData("SIMULATOR_STATE_CHANGED_EVENT",
            "simulator started or stopped", new ObjectDescriptor[] {new ObjectDescriptor("New simulator state",
                    "New simulator state; true if running; false if stopped", Boolean.class)}));

    /**
     * Construct a new SimulatorStateTransceiver.
     * @param simulator OtsSimulatorInterface; the simulator
     * @throws RemoteException on network error
     */
    public SimulatorStateTransceiver(final OtsSimulatorInterface simulator) throws RemoteException
    {
        super("Simulator state transceiver", MetaData.EMPTY,
                new MetaData("SIMULATOR_STATE_CHANGED_EVENT", "simulator state changed"));
        this.simulator = simulator;
        this.eventMultiplexer = new EventMultiplexer(simulator);
    }

    /** {@inheritDoc} */
    @Override
    public Object[] get(final Object[] address, final ReturnWrapper returnWrapper)
            throws RemoteException, Sim0MQException, SerializationException
    {
        AbstractTransceiver.verifyMetaData(MetaData.EMPTY, address);
        String result = null;
        if (this.simulator.isInitialized())
        {
            if (this.simulator.isStartingOrRunning())
            {
                result = "Starting or running";
            }
            else
            {
                result = "Stopping or stopped";
            }
        }
        else
        {
            result = "Not (yet) initialized";
        }
        return new Object[] {result};
    }

    /** Result of the getLookupEventProducer method. */
    private LookupEventProducer lepi = new LookupEventProducer()
    {
        @Override
        public EventProducer lookup(final Object[] address, final ReturnWrapper returnWrapper)
                throws Sim0MQException, SerializationException
        {
            String bad = AbstractTransceiver.verifyMetaData(MetaData.EMPTY, address);
            if (null != bad)
            {
                returnWrapper.nack(bad);
                return null;
            }
            return SimulatorStateTransceiver.this.eventMultiplexer;
        }

        @Override
        public MetaData getAddressMetaData()
        {
            return MetaData.EMPTY;
        }
    };

    /**
     * Retrieve the event LookupEventProducer.
     * @return EventProducerInterface; the event multiplexer
     */
    public LookupEventProducer getLookupEventProducer()
    {
        return this.lepi;
    }

}

/**
 * Create a subscription to SimulatorInterface.START_EVENT and SimulatorInterface.STOP_EVENT and emit a SIMULATOR_STATE_CHANGED
 * event for each.
 */
class EventMultiplexer extends LocalEventProducer implements EventListener
{
    /** ... */
    private static final long serialVersionUID = 20200618L;

    /**
     * @param simulator OtsSimulatorInterface; the simulator
     * @throws RemoteException on network error
     */
    EventMultiplexer(final OtsSimulatorInterface simulator) throws RemoteException
    {
        simulator.addListener(this, SimulatorInterface.START_EVENT);
        simulator.addListener(this, SimulatorInterface.STOP_EVENT);
    }

    /** {@inheritDoc} */
    @Override
    public void notify(final Event event) throws RemoteException
    {
        notifyTimedEvent(event);
    }

    /**
     * Avoid a compilation error with Java 11. Could be bug https://bugs.openjdk.java.net/browse/JDK-8206142 which is
     * unsolved...
     * @param <C> the casting class for the event timestamp
     * @param event the event to be notified of
     */
    private <C extends Serializable & Comparable<C>> void notifyTimedEvent(final Event event)
    {
        @SuppressWarnings("unchecked")
        TimedEvent<C> timedEvent = (TimedEvent<C>) event;
        fireTimedEvent(SimulatorStateTransceiver.SIMULATOR_STATE_CHANGED,
                event.getType().equals(SimulatorInterface.START_EVENT), timedEvent.getTimeStamp());
    }

}