Something went wrong on our end
-
Terry D. Norbraten authoredTerry D. Norbraten authored
TwoCraneBerthsOpenDis7.java 15.69 KiB
package SimkitOpenDis7Examples;
import edu.nps.moves.dis7.enumerations.DisPduType;
import edu.nps.moves.dis7.enumerations.VariableRecordType;
import edu.nps.moves.dis7.pdus.EntityStatePdu;
import edu.nps.moves.dis7.pdus.EulerAngles;
import edu.nps.moves.dis7.pdus.Vector3Double;
import edu.nps.moves.dis7.utilities.DisChannel;
import edu.nps.moves.dis7.utilities.PduFactory;
import java.util.SortedSet;
import java.util.TreeSet;
import simkit.Priority;
import simkit.SimEntityBase;
/**
* Add DIS outputs to TwoCraneBerths simkit simulation
* @see SimkitOpenDis7Examples.TwoCraneBerths
* @see <a href="run/RunTwoCranesBerthOpenDis7Log.txt" target="_blank">RunTwoCranesBerthOpenDis7Log.txt</a>
* @see <a href="TwoCraneBerthsAssignment05.docx" target="_blank">TwoCraneBerthsAssignment05.docx</a>
* @see <a href="TwoCraneBerthsAssignment05Solution.docx" target="_blank">TwoCraneBerthsAssignment05Solution.docx</a>
* @author abuss@nps.edu
* @author brutzman@nps.edu
*/
public class TwoCraneBerthsOpenDis7 extends SimEntityBase
{
// Utility constructor method: initial descriptor and verboseness of disNetworkInterface, pduRecorder
private final DisChannel disChannel = new DisChannel("TwoCraneBerthsOpenDis7", false, true);
PduFactory pduFactory = new PduFactory();
EntityStatePdu espduCrane = (EntityStatePdu) pduFactory.createPdu(DisPduType.ENTITY_STATE);
/**
* Queue of Ships waiting to go into the berth
*/
protected SortedSet<Ship> queue;
/**
* Contains 0, 1, or two Ships being unloaded
*/
protected SortedSet<Ship> berth;
/**
* Time in the system for each Ship
*/
protected double timeInSystem;
/**
* Delay in the queue for each Ship
*/
protected double delayInQueue;
/**
* number of Ships offloaded
*/
protected int shipCount = 0;
/**
* Instantiate queue and berth containers
*/
public TwoCraneBerthsOpenDis7() {
this.queue = new TreeSet<>();
this.berth = new TreeSet<>();
}
/**
* Clear queue and berth containers
*/
@Override
public void reset() {
super.reset();
queue.clear();
berth.clear();
timeInSystem = Double.NaN; // Not a Number
delayInQueue = Double.NaN; // Not a Number
}
/**
* Only PropertyChangeEvents
*/
public void doRun()
{
disChannel.setVerboseComments(true);
disChannel.setVerboseDisNetworkInterface(true);
firePropertyChange("queue", getQueue());
firePropertyChange("berth", getBerth());
firePropertyChange("timeInSystem", getTimeInSystem());
firePropertyChange("delayInQueue", getDelayInQueue());
}
/**
* Add the given Ship to queue<br>
* If berths is empty, schedule StartUnloadingTwoCranes<br>
* If berths has 1 Ship, schedule SwitchToOneCrane
*
* @param ship Given Ship arriving to harbor
*/
public void doArrival(Ship ship) {
ship.stampTime();
SortedSet<Ship> oldQueue = getQueue();
queue.add(ship);
firePropertyChange("queue", oldQueue, getQueue());
if (berth.isEmpty()) {
waitDelay("StartUnloadingTwoCranes", 0.0, Priority.HIGH);
}
if (berth.size() == 1) {
waitDelay("SwitchToOneCrane", 0.0);
}
}
/**
* Remove the first Ship from queue<br>
* DelayInQueue is elapsedTime of that Ship<br>
* Add the ship to berth container<br>
* Schedule EndUnloadingTwoCranes at half the remaining time
*/
public void doStartUnloadingTwoCranes() {
SortedSet<Ship> oldQueue = getQueue();
Ship ship = queue.first();
queue.remove(ship);
firePropertyChange("queue", oldQueue, getQueue());
delayInQueue = ship.getElapsedTime();
firePropertyChange("delayInQueue", getDelayInQueue());
ship.stampTime();
SortedSet<Ship> oldBerth = getBerth();
berth.add(ship);
firePropertyChange("berth", oldBerth, getBerth());
// TODO reportCraneContainerUnloadOperationsDIS()
waitDelay("EndUnloadingTwoCranes", 0.5 * ship.getRemainingUnloadingTime());
}
/**
* Remove the (one) Ship from berth<br>
* TimeInSystem is the age of the Ship
*/
public void doEndUnloadingTwoCranes() {
SortedSet<Ship> oldBerth = getBerth();
Ship ship = berth.first();
berth.remove(ship);
firePropertyChange("berth", oldBerth, getBerth());
timeInSystem = ship.getAge();
firePropertyChange("timeInSystem", getTimeInSystem());
}
/**
* This event is when a Ship arrives to find only one other Ship being
* unloaded.<br>
* Credit the ship in the berth with work at a rate of 2 (since 2 cranes
* have been unloading it<br>
* Interrupt EndUnloadingTwoCranes<br>
* Schedule EndUnloadingOneCrane with the Ship already in the berth<br>
* Schedule StartUnloadingOneCrane
*/
public void doSwitchToOneCrane() {
Ship ship = berth.first();
ship.work(2.0);
ship.stampTime();
interrupt("EndUnloadingTwoCranes");
waitDelay("EndUnloadingOneCrane", ship.getRemainingUnloadingTime(), ship);
waitDelay("StartUnloadingOneCrane", 0.0, Priority.HIGH);
}
/**
* Pop the first Ship from the queue<br>
* delayInQueue is elapsedTime (from Arrival event)<br>
* Add that Ship to berth container<br>
* Schedule EndUnloadingOneCrane with that Ship
*/
public void doStartUnloadingOneCrane() {
SortedSet<Ship> oldQueue = getQueue();
Ship ship = queue.first();
queue.remove(ship);
firePropertyChange("queue", oldQueue, getQueue());
delayInQueue = ship.getElapsedTime();
firePropertyChange("delayInQueue", getDelayInQueue());
ship.stampTime();
SortedSet<Ship> oldBerth = getBerth();
berth.add(ship);
firePropertyChange("berth", oldBerth, getBerth());
// log crane operations for each ship
shipCount++;
if (shipCount == 1)
System.out.println("=====================================");
if (shipCount <= 2)
{
reportCraneContainerUnloadOperationsDIS( ship.getTimeStamp(), // simkit timeStamp
10, // numberOfContainers
90.0 // initial position of Ship
); // TODO indicate berth
System.out.println("=====================================");
}
waitDelay("EndUnloadingOneCrane", ship.getRemainingUnloadingTime(), ship);
}
/**
* Perform crane container unloading operations and send PDUs to report progress
* @param simkitTimeStamp simkit timeStamp when crane operations began
* @param numberContainers how many container boxes to offload
* @param pierDistanceForCraneOffload Y coordinate down pier across from ship's berth, aligned with cargo
* @see <a href="https://en.wikipedia.org/wiki/The_Box_(Levinson_book)" target="_blank">The Box: How the Shipping Container Made the World Smaller and the World Economy Bigger is</a>
*/
public void reportCraneContainerUnloadOperationsDIS(
double simkitTimeStamp,
// which crane
// which berth
int numberContainers,
double pierDistanceForCraneOffload
// lengthOfCrane
)
{
int disTimeStamp = (int)simkitTimeStamp; // TODO document relationship
// Pier coordinate system follows, Right-Hand Rule (RHR) throughout:
// +X axis to right with X=0 on centerline of pier (train tracks
// +Y axis down pier with Y=0 at head of pier,
// +Z axis vertical with Z=0 at level of pier
// phi is rotation about +X axis, radians
// theta is rotation about +Y axis, radians
// psi is rotation about +X axis, radians
Vector3Double positionPierHead = new Vector3Double();
// Vector3Double positionPierHead = new Vector3Double(0.0, 0.0, 0.0); // TODO needs utility constructor
Vector3Double positionPierOffload = new Vector3Double();
positionPierOffload.setY(pierDistanceForCraneOffload);
double craneLinearSpeed = 1.0; // m/sec
double craneRotationRate = 10.0; // degrees/second
double containerHookupDuration = 60.0; // seconds average
double containerDetachDuration = 30.0; // seconds average
// not modeling crane elevation angle, only orientation of crane cab and gantry
EulerAngles orientationToShip = (new EulerAngles()).setPsi((float) (90.0 * Math.PI/180.0)); // TODO utility methods needed
EulerAngles orientationToPier = (new EulerAngles()).setPsi((float) ( 0.0 * Math.PI/180.0)); // TODO utility methods needed
// initialize ESPDU
// espduCrane.reset(); // TODO intialization utility method
// espduCrane.setEntityAppearance(TODO); // highlight active crane?
espduCrane.setTimestamp(disTimeStamp);
// 1. Crane at head of pier, operator present, boom elevated vertically in stow position
espduCrane.setEntityLocation(positionPierHead);
espduCrane.setEntityOrientation(orientationToPier);
disChannel.sendSinglePdu (disTimeStamp, espduCrane);
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Ready to process Ship " + shipCount + " with crane at head of pier");
// 2, Ship arrives, tug departs
// ship PDU already, TODO can track tugboat here if desired for further fidelity
// 3. Crane moves to position of ship: travel to positionOfCrane ay craneLinearSpeed
double craneTravelDuration = pierDistanceForCraneOffload / craneLinearSpeed; // units analysis: meters / (meters/second) = seconds
espduCrane.setEntityLocation(pierDistanceForCraneOffload, 0.0, 0.0);
disChannel.sendSinglePdu(disTimeStamp, espduCrane);
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO,
"Crane moved to position " + pierDistanceForCraneOffload + "m for offload after " + craneTravelDuration + " seconds");
// 4. repeat until done: Crane rotates, lift container, rotates, lower container
for (int containerIndex = 1; containerIndex <= numberContainers; containerIndex++)
{
// 4.a crane rotates to ship
double craneRotationDelay = 90.0 / craneRotationRate; // units analysis: degrees / (degrees/second) = seconds
espduCrane.setEntityOrientation(orientationToShip);
disChannel.sendSinglePdu(disTimeStamp, espduCrane);
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Crane oriented to ship after " + craneRotationDelay + " seconds" +
" with craneRotationRate=" + craneRotationRate + " degrees/second");
// // 4.b announce next step without further delay, then perform container hookup
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Hooking up Container " + containerIndex + " to crane has started..."); // TODO + whichCrane
disTimeStamp += containerHookupDuration;
// disChannel.sendSinglePdu(disTimeStamp, espduCrane); // superfluous, crane hasn't moved
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Hooked up: Container " + containerIndex + " to crane" // TODO + whichCrane
+ " after " + craneRotationDelay + " seconds");
// 4.c crane rotates to pier
disTimeStamp += craneRotationDelay;
espduCrane.setEntityOrientation(orientationToPier);
disChannel.sendSinglePdu(disTimeStamp, espduCrane);
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Crane oriented to pier after " + craneRotationDelay + " seconds" +
" with craneRotationRate=" + craneRotationRate + " degrees/second");
// 4.d crane unhooks container
disTimeStamp += containerDetachDuration;
// disChannel.sendSinglePdu(disTimeStamp, espduCrane); // superfluous, crane hasn't moved
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.CARGO, "Crane detached: Container " + containerIndex + " on pier" // TODO + whichCrane
+ " after " + containerDetachDuration + " seconds");
// send PDUs accordingly
double totalDelay = (2.0 * craneRotationDelay + containerHookupDuration + containerDetachDuration);
disChannel.sendCommentPdu(disTimeStamp, VariableRecordType.ELAPSED_TIME,"Time duration for crane moving container " + containerIndex + ": " + totalDelay + " seconds");
// Future work: if this container is special, meaning on watchlist, report it
}
// 4. Crane stows boom in vertical position
// TODO future refinement if even-more fidelity is desired
// 5. Crane returns to head of pier every time in order to avoid interference with ship mooring/unmooring
espduCrane.setEntityLocation(positionPierHead); // head of pier
disTimeStamp += craneTravelDuration; // travel time back to head of pier
espduCrane.setEntityLocation(positionPierHead);
disChannel.sendSinglePdu(disTimeStamp, espduCrane);
}
/**
* Shutdown DIS network interfaces, enabling program termination
*/
public void shutdownDisChannel()
{
if (disChannel != null)
{
disChannel.leave();
disChannel.tearDownNetworkInterface();
}
}
/**
* Remove given Ship from berth<br>
* If Ships in queue, schedule StartUnloadingOneCrane<br>
* If queue is empty, schedule SwitchToTwoCranes<br>
* timeInSystem is age of Ship
*
* @param ship Given Ship
*/
public void doEndUnloadingOneCrane(Ship ship) {
SortedSet<Ship> oldBerth = getBerth();
berth.remove(ship);
firePropertyChange("berth", oldBerth, getBerth());
if (!queue.isEmpty()) {
waitDelay("StartUnloadingOneCrane", 0.0, Priority.HIGH);
}
if (queue.isEmpty() && berth.size() == 1) {
waitDelay("SwitchToTwoCranes", 0.0);
}
timeInSystem = ship.getAge();
firePropertyChange("timeInSystem", getTimeInSystem());
}
/**
* Credit the work of the remaining Ship in berth at unit rate<br>
* Interrupt EndUnloadingOneCrane<br>
* Schedule EndUnloadingTwoCranes at double the rate (i.e., half the remaining time)
*/
public void doSwitchToTwoCranes() {
Ship ship = berth.first();
ship.work(1.0);
ship.stampTime();
interrupt("EndUnloadingOneCrane", ship);
waitDelay("EndUnloadingTwoCranes",
0.5 * ship.getRemainingUnloadingTime());
}
/**
* Get tree of sorted Ship set queue
* @return Shallow copy of queue
*/
public SortedSet<Ship> getQueue() {
return new TreeSet<>(queue);
}
/**
* Get tree of sorted Ship set berths
* @return Shallow copy of berth
*/
public SortedSet<Ship> getBerth() {
return new TreeSet<>(berth);
}
/**
* accessor method to get a state variable
* @return The timeInSystem
*/
public double getTimeInSystem() {
return timeInSystem;
}
/**
* accessor method to get a state variable
* @return The delayInQueue
*/
public double getDelayInQueue() {
return delayInQueue;
}
}