Resource.java
package nl.tudelft.simulation.dsol.formalisms;
import java.rmi.RemoteException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
import org.djutils.base.Identifiable;
import org.djutils.event.EventType;
import org.djutils.event.LocalEventProducer;
import org.djutils.exceptions.Throw;
import org.djutils.metadata.MetaData;
import org.djutils.metadata.ObjectDescriptor;
import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.simulators.DevsSimulatorInterface;
/**
* A resource defines a shared and limited amount.
* <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>
* @param <T> the simulation time type
*/
public class Resource<T extends Number & Comparable<T>> extends LocalEventProducer implements Identifiable
{
/** */
private static final long serialVersionUID = 20140805L;
/** the id of the resource. */
private String resourceId;
/** the counter to give a unique sequence number to the requests. */
private static long counter = 0;
/** UTILIZATION_EVENT is fired on activity that decreases or increases the utilization. */
public static final EventType UTILIZATION_EVENT = new EventType(new MetaData("UTILIZATION_EVENT",
"Utilization changed", new ObjectDescriptor("newUtilization", "new utilization", Double.class)));
/** QUEUE_LENGTH_EVENT fired on changes in queue length. */
public static final EventType QUEUE_LENGTH_EVENT =
new EventType(new MetaData("QUEUE_LENGTH_EVENT", "Queue length changed",
new ObjectDescriptor("newQueueLength", "new queue length", Integer.class)));
/** QUEUE_TIME_EVENT is fired wwhen a request is granted and provides the waiting time (which can be 0). */
public static final EventType QUEUE_WAITING_TIME_EVENT =
new EventType(new MetaData("QUEUE_WAITING_TIME_EVENT", "Queue waiting time",
new ObjectDescriptor("queue waiting time", "queue waiting time", Number.class)));
/** the minimum priority. */
public static final int MIN_REQUEST_PRIORITY = 0;
/** the maximum priority. */
public static final int MAX_REQUEST_PRIORITY = 10;
/** the default average priority. */
public static final int DEFAULT_REQUEST_PRIORITY = 5;
/** capacity defines the maximum capacity of the resource. */
private double capacity;
/** claimedCapacity defines the currently claimed capacity. */
private double claimedCapacity = 0.0;
/** request defines the list of requestors for this resource. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected final SortedSet<Request<T>> requests;
/** simulator defines the simulator on which is scheduled. */
@SuppressWarnings("checkstyle:visibilitymodifier")
protected DevsSimulatorInterface<T> simulator;
/**
* Create a new Resource with a capacity and a specific request comparator, e.g., LIFO or sorted on an attribute.
* @param resourceId String; the id of this resource
* @param simulator DevsSimulatorInterface<T>; the simulator
* @param capacity double; the capacity of the resource
* @param requestComparator Comparator<Request<T>>; the comparator to use
*/
public Resource(final String resourceId, final DevsSimulatorInterface<T> simulator, final double capacity,
final Comparator<Request<T>> requestComparator)
{
Throw.whenNull(resourceId, "resourceId cannot be null");
Throw.whenNull(simulator, "simulator cannot be null");
Throw.whenNull(requestComparator, "requestComparator cannot be null");
this.resourceId = resourceId;
this.simulator = simulator;
this.capacity = capacity;
this.requests = Collections.synchronizedSortedSet(new TreeSet<Request<T>>(requestComparator));;
}
/**
* Create a new Resource with a capacity and a default FIFO request comparator.
* @param resourceId String; the id of this resource
* @param simulator DevsSimulatorInterface<T>; the simulator
* @param capacity double; the capacity of the resource
*/
public Resource(final String resourceId, final DevsSimulatorInterface<T> simulator, final double capacity)
{
this(resourceId, simulator, capacity, new RequestComparator<T>());
}
/** {@inheritDoc} */
@Override
public String getId()
{
return this.resourceId;
}
/**
* returns the maximum, and thus original capacity of the resource.
* @return capacity the maximum, and thus original capacity of the resource.
*/
public double getCapacity()
{
return this.capacity;
}
/**
* returns the amount of currently claimed capacity.
* @return the amount of currently claimed capacity.
*/
public double getClaimedCapacity()
{
return this.claimedCapacity;
}
/**
* returns the currently available capacity on this resource. This method is implemented as
* <code>return this.getCapacity()-this.getClaimedCapacity()</code>
* @return the currently available capacity on this resource.
*/
public double getAvailableCapacity()
{
return this.capacity - this.claimedCapacity;
}
/**
* returns the number of instances currently waiting for this resource.
* @return the number of instances currently waiting for this resource
*/
public int getQueueLength()
{
return this.requests.size();
}
/**
* Method alterClaimedCapacity.
* @param amount double; refers the amount which is added to the claimed capacity
* @throws RemoteException on network failure
*/
private synchronized void alterClaimedCapacity(final double amount) throws RemoteException
{
this.claimedCapacity += amount;
this.fireTimedEvent(Resource.UTILIZATION_EVENT, this.claimedCapacity, this.simulator.getSimulatorTime());
}
/**
* sets the capacity of the resource.
* @param capacity double; the new maximal capacity
*/
public void setCapacity(final double capacity)
{
this.capacity = capacity;
try
{
this.releaseCapacity(0.0);
}
catch (RemoteException remoteException)
{
// This exception cannot occur.
this.simulator.getLogger().always().warn(remoteException, "setCapacity");
}
}
/**
* requests an amount of capacity from the resource.
* @param amount double; the requested amount
* @param requestor ResourceRequestorInterface<T>; the RequestorInterface requesting the amount
* @throws RemoteException on network failure
* @throws SimRuntimeException on other failures
*/
public synchronized void requestCapacity(final double amount, final ResourceRequestorInterface<T> requestor)
throws RemoteException, SimRuntimeException
{
this.requestCapacity(amount, requestor, Resource.DEFAULT_REQUEST_PRIORITY);
}
/**
* requests an amount of capacity from the resource.
* @param amount double; the requested amount
* @param requestor ResourceRequestorInterface<T>; the RequestorInterface requesting the amount
* @param priority int; the priority of the request
* @throws RemoteException on network failure
* @throws SimRuntimeException on other failures
*/
public synchronized void requestCapacity(final double amount, final ResourceRequestorInterface<T> requestor,
final int priority) throws RemoteException, SimRuntimeException
{
if (amount < 0.0)
{
throw new SimRuntimeException("requested capacity on resource cannot <0.0");
}
if ((this.claimedCapacity + amount) <= this.capacity)
{
this.alterClaimedCapacity(amount);
this.simulator.scheduleEventNow(requestor, "receiveRequestedResource", new Object[] {Double.valueOf(amount), this});
}
else
{
synchronized (this.requests)
{
this.requests.add(new Request<T>(requestor, amount, priority));
}
this.fireTimedEvent(Resource.QUEUE_LENGTH_EVENT, this.requests.size(),
this.simulator.getSimulatorTime());
}
}
/**
* releases an amount of capacity from the resource.
* @param amount double; the amount to release
* @throws RemoteException on network failure
*/
public void releaseCapacity(final double amount) throws RemoteException
{
if (amount < 0.0)
{
throw new IllegalArgumentException("released capacity on resource cannot <0.0");
}
if (amount > 0.0)
{
this.alterClaimedCapacity(-Math.min(this.capacity, amount));
}
synchronized (this.requests)
{
for (Iterator<Request<T>> i = this.requests.iterator(); i.hasNext();)
{
Request<T> request = i.next();
if ((this.capacity - this.claimedCapacity) >= request.getAmount())
{
this.alterClaimedCapacity(request.getAmount());
request.getRequestor().receiveRequestedResource(request.getAmount(), this);
synchronized (this.requests)
{
i.remove();
}
this.fireTimedEvent(Resource.QUEUE_LENGTH_EVENT, this.requests.size(),
this.simulator.getSimulatorTime());
}
else
{
return;
}
}
}
}
/** {@inheritDoc} */
@Override
public String toString()
{
return this.resourceId;
}
/**
* the RequestComparator. This comparator first checks on priority, then on ID.
* @param <T> the simulation time type to use.
*/
public static class RequestComparator<T extends Number & Comparable<T>> implements Comparator<Request<T>>
{
/** {@inheritDoc} */
@Override
public int compare(final Request<T> arg0, final Request<T> arg1)
{
if (arg0.getPriority() > arg1.getPriority())
{
return -1;
}
if (arg0.getPriority() < arg1.getPriority())
{
return 1;
}
if (arg0.getId() < arg1.getId())
{
return -1;
}
if (arg0.getId() > arg1.getId())
{
return 1;
}
return 0;
}
}
/**
* A Request.
* @param <T> the simulation time type to use.
*/
public static class Request<T extends Number & Comparable<T>>
{
/** the priority of the request. */
private int priority = 5;
/** the number of this request. */
private long id = -1;
/** requestor the resourceRequestor. */
private ResourceRequestorInterface<T> requestor;
/** amount is the amount requested by the resource. */
private double amount;
/**
* constructs a new Request.
* @param requestor ResourceRequestorInterface<T>; the requestor
* @param amount double; the requested amount
* @param priority int; the priority of the request
*/
public Request(final ResourceRequestorInterface<T> requestor, final double amount, final int priority)
{
this.requestor = requestor;
this.amount = amount;
this.priority = priority;
Resource.counter++;
this.id = Resource.counter;
}
/**
* gets the requested amount.
* @return the requested amount
*/
public double getAmount()
{
return this.amount;
}
/**
* gets the requestor.
* @return the Requestor.
*/
public ResourceRequestorInterface<T> getRequestor()
{
return this.requestor;
}
/**
* returns the priority of the request.
* @return the priority
*/
public int getPriority()
{
return this.priority;
}
/**
* returns the id of the request.
* @return the id
*/
public long getId()
{
return this.id;
}
/** {@inheritDoc} */
@Override
public String toString()
{
return "RequestForResource[requestor=" + this.requestor + ";amount=" + this.amount + ";priority=" + this.priority
+ "]";
}
}
}