/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada Inc. * Copyright (C) 2016 JaamSim Software Inc. * * 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 com.jaamsim.ProcessFlow; import java.util.ArrayList; import com.jaamsim.Graphics.DisplayEntity; import com.jaamsim.Graphics.PolylineInfo; import com.jaamsim.Samples.SampleConstant; import com.jaamsim.Samples.SampleInput; import com.jaamsim.input.ColourInput; import com.jaamsim.input.Input; import com.jaamsim.input.Keyword; import com.jaamsim.input.ValueInput; import com.jaamsim.math.MathUtils; import com.jaamsim.math.Vec3d; import com.jaamsim.units.DimensionlessUnit; import com.jaamsim.units.TimeUnit; /** * Moves one or more Entities along a path at a constant speed. */ public class EntityConveyor extends LinkedService { @Keyword(description = "The travel time for the conveyor.", exampleList = {"10.0 s"}) private final SampleInput travelTimeInput; @Keyword(description = "The width of the conveyor in pixels.", exampleList = {"1"}) private final ValueInput widthInput; @Keyword(description = "The colour of the conveyor.", exampleList = {"red"}) private final ColourInput colorInput; private final ArrayList<ConveyorEntry> entryList; // List of the entities being conveyed private double presentTravelTime; { operatingThresholdList.setHidden(true); waitQueue.setHidden(true); match.setHidden(true); processPosition.setHidden(true); forcedMaintenanceList.setHidden(true); forcedBreakdownList.setHidden(true); travelTimeInput = new SampleInput("TravelTime", "Key Inputs", new SampleConstant(0.0d)); travelTimeInput.setValidRange(0.0, Double.POSITIVE_INFINITY); travelTimeInput.setUnitType(TimeUnit.class); travelTimeInput.setEntity(this); this.addInput(travelTimeInput); widthInput = new ValueInput("Width", "Key Inputs", 1.0d); widthInput.setUnitType(DimensionlessUnit.class); widthInput.setValidRange(1.0d, Double.POSITIVE_INFINITY); this.addInput(widthInput); colorInput = new ColourInput("Color", "Key Inputs", ColourInput.BLACK); this.addInput(colorInput); this.addSynonym(colorInput, "Colour"); } public EntityConveyor() { entryList = new ArrayList<>(); } @Override public void earlyInit() { super.earlyInit(); presentTravelTime = travelTimeInput.getValue().getNextSample(0.0); entryList.clear(); } private static class ConveyorEntry { final DisplayEntity entity; double position; public ConveyorEntry(DisplayEntity ent, double pos) { entity = ent; position = pos; } @Override public String toString() { return String.format("(%s, %.6f)", entity, position); } } @Override public void addEntity(DisplayEntity ent ) { super.addEntity(ent); double simTime = this.getSimTime(); // Update the positions of the entities on the conveyor this.updateProgress(); // Update the travel time this.updateTravelTime(simTime); // Add the entity to the conveyor ConveyorEntry entry = new ConveyorEntry(ent, 0.0d); entryList.add(entry); // If necessary, wake up the conveyor this.startStep(); } @Override protected boolean startProcessing(double simTime) { return !entryList.isEmpty(); } @Override protected boolean processStep(double simTime) { // Remove the entity from the conveyor DisplayEntity ent = entryList.remove(0).entity; // Update the travel time this.updateTravelTime(simTime); // Send the entity to the next component this.sendToNextComponent(ent); return true; } @Override protected double getStepDuration(double simTime) { // Calculate the time for the first entity to reach the end of the conveyor double dt = simTime - this.getLastUpdateTime(); double dur = (1.0d - entryList.get(0).position)*presentTravelTime - dt; dur = Math.max(dur, 0); // Round-off to the nearest tick can cause a negative value if (isTraceFlag()) trace(1, "getProcessingTime = %.6f", dur); return dur; } @Override public void updateProgress(double dt) { if (presentTravelTime == 0.0d) return; // Calculate the fractional distance travelled since the last update double frac = dt/presentTravelTime; if (MathUtils.near(frac, 0.0d)) return; // Increment the positions of the entities on the conveyor if (isTraceFlag()) traceLine(2, "BEFORE - entryList=%s", entryList); for (ConveyorEntry entry : entryList) { entry.position += frac; } if (isTraceFlag()) traceLine(2, "AFTER - entryList=%s", entryList); } private void updateTravelTime(double simTime) { // Has the travel time changed? double newTime = travelTimeInput.getValue().getNextSample(simTime); if (newTime != presentTravelTime) { if (isTraceFlag()) { trace(1, "updateTravelTime"); traceLine(2, "newTime=%.6f, presentTravelTime=%.6f", newTime, presentTravelTime); } // Set the new travel time presentTravelTime = newTime; // Adjust the time at which the next entity will reach the end of the conveyor // (required when an entity is added to a conveyor that already has entities in flight) this.resetProcess(); } } // ******************************************************************************************** // GRAPHICS // ******************************************************************************************** @Override public void updateForInput(Input<?> in) { super.updateForInput(in); // If Points were input, then use them to set the start and end coordinates if (in == pointsInput || in == colorInput || in == widthInput) { invalidateScreenPoints(); return; } } @Override public void updateGraphics(double simTime) { if (!this.isBusy() || presentTravelTime == 0.0d) return; // Move each entity on the conveyor to its present position double frac = (simTime - this.getLastUpdateTime())/presentTravelTime; for (int i=0; i<entryList.size(); i++) { ConveyorEntry entry = entryList.get(i); Vec3d localPos = this.getPositionOnPolyline(simTime, entry.position + frac); entry.entity.setGlobalPosition(this.getGlobalPosition(localPos)); } } @Override public PolylineInfo[] buildScreenPoints(double simTime) { int w = Math.max(1, widthInput.getValue().intValue()); PolylineInfo[] ret = new PolylineInfo[1]; ret[0] = new PolylineInfo(pointsInput.getValue(), getCurveType(), colorInput.getValue(), w); return ret; } // LinkDisplayable overrides @Override public Vec3d getSourcePoint() { ArrayList<Vec3d> points = pointsInput.getValue(); if (points.size() == 0) { return getGlobalPosition(); } return new Vec3d(points.get(points.size()-1)); } @Override public Vec3d getSinkPoint() { ArrayList<Vec3d> points = pointsInput.getValue(); if (points.size() == 0) { return getGlobalPosition(); } return new Vec3d(points.get(0)); } @Override public double getRadius() { return 0.2; // TODO: make this a tunable parameter } }