diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 37a7509..d23e771 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,9 @@ - + + + diff --git a/app/src/androidTest/java/de/hems/trafficsim/ExampleInstrumentedTest.java b/app/src/androidTest/java/de/hems/trafficsim/ExampleInstrumentedTest.java deleted file mode 100644 index 9c45982..0000000 --- a/app/src/androidTest/java/de/hems/trafficsim/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.hems.trafficsim; - -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("de.hems.trafficsim", appContext.getPackageName()); - } -} \ No newline at end of file diff --git a/app/src/main/java/de/hems/trafficsim/MainActivity.java b/app/src/main/java/de/hems/trafficsim/MainActivity.java index 1fda53f..d23c0fe 100644 --- a/app/src/main/java/de/hems/trafficsim/MainActivity.java +++ b/app/src/main/java/de/hems/trafficsim/MainActivity.java @@ -1,7 +1,5 @@ package de.hems.trafficsim; -import androidx.appcompat.app.AppCompatActivity; - import android.os.Bundle; import android.view.View; import android.widget.Button; @@ -9,79 +7,143 @@ import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; -import java.util.Observable; -import java.util.Observer; +import androidx.appcompat.app.AppCompatActivity; +/** + * Main user interface class, containing all necessary gui elements and their control flow. + */ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener { + /** + * default value of the number of vehicles on the track + */ public static final int defaultNoOfVehicles = 25; + + /** + * default value of the length of the track + */ public static final int defaultTrackLength = 100; + + /** + * default value of brake probability + */ public static final float defaultBrakeProb = 0.3f; + + /** + * default value of the maximum velocity of the vehicles + */ public static final float defaultMaxVelocity = 5.0f; + + /** + * default value of the delay between two simulation steps + */ public static final int defaultDelay = 0; + + /** + * default value of the number of vehicles on the track + */ public static final int defaultHistoryLength = 50; + + /** + * default value of the number of vehicles on the track + */ public static final int defaultFrameskip = 0; + + /** + * the track to show in the activity + */ protected Track track; + + /** + * the surface view on which the renderer draws the track history + */ protected TimeRecordView trackView; + + /** + * the thread which runs the simulation and visualization + */ protected Worker worker; + + /** + * the renderer instance drawing the track history + */ protected Renderer renderer; + + /** + * the layout which keeps the surface view + */ protected LinearLayout viewStack; + /** + * Utility function to round a float to a given amount of digits. + * + * @param number number to round + * @param digits amount of digits + * @return rounded number + */ + public static float round(float number, int digits) { + float div = (float) Math.pow(10.0f, digits); + return Math.round(number * div) / div; + } + + /** + * Constructor for MainActivity + * + * @param savedInstanceState Bundle with previously saved activity state, otherwise null + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.track = new Track(defaultNoOfVehicles, defaultTrackLength, defaultBrakeProb, defaultMaxVelocity, defaultDelay, defaultHistoryLength); - this.viewStack = (LinearLayout) findViewById(R.id.trackViewStack); + this.viewStack = findViewById(R.id.trackViewStack); this.trackView = new TimeRecordView(this, track); viewStack.addView(this.trackView); this.renderer = new Renderer(track, this.trackView.getHolder()); - SeekBar trackLengthSeekBar = (SeekBar) findViewById(R.id.trackLengthSeekBar); + SeekBar trackLengthSeekBar = findViewById(R.id.trackLengthSeekBar); trackLengthSeekBar.setOnSeekBarChangeListener(this); trackLengthSeekBar.setProgress(defaultTrackLength); ((TextView)(findViewById(R.id.trackLengthTextView))).setText(String.valueOf(defaultTrackLength)); - SeekBar maxVelocitySeekBar = (SeekBar) findViewById(R.id.maxVelocitySeekBar); + SeekBar maxVelocitySeekBar = findViewById(R.id.maxVelocitySeekBar); maxVelocitySeekBar.setOnSeekBarChangeListener(this); maxVelocitySeekBar.setProgress((int)defaultMaxVelocity); ((TextView)(findViewById(R.id.maxVeloTextView))).setText(String.valueOf((int)defaultMaxVelocity)); - SeekBar noOfVehiclesSeekBar = (SeekBar) findViewById(R.id.noOfVehiclesSeekBar); + SeekBar noOfVehiclesSeekBar = findViewById(R.id.noOfVehiclesSeekBar); noOfVehiclesSeekBar.setOnSeekBarChangeListener(this); noOfVehiclesSeekBar.setProgress(defaultNoOfVehicles); ((TextView)(findViewById(R.id.noOfVehiclesTextView))).setText(String.valueOf(defaultNoOfVehicles)); - SeekBar brakeProbabilitySeekBar = (SeekBar) findViewById(R.id.brakeProbabilitySeekBar); + SeekBar brakeProbabilitySeekBar = findViewById(R.id.brakeProbabilitySeekBar); brakeProbabilitySeekBar.setOnSeekBarChangeListener(this); brakeProbabilitySeekBar.setProgress((int)(defaultBrakeProb*20)); ((TextView)(findViewById(R.id.brakeProbTextView))).setText(String.valueOf(defaultBrakeProb)); - SeekBar delaySeekBar = (SeekBar)(findViewById(R.id.simDelaySeekBar)); + SeekBar delaySeekBar = findViewById(R.id.simDelaySeekBar); delaySeekBar.setOnSeekBarChangeListener(this); delaySeekBar.setProgress(defaultDelay); - ((TextView)(findViewById(R.id.simDelayTextView))).setText(String.valueOf(defaultDelay)); + ((TextView) (findViewById(R.id.simDelayTextView))).setText(String.valueOf(defaultDelay)); - SeekBar frameskipSeekBar = (SeekBar)(findViewById(R.id.frameskipSeekBar)); + SeekBar frameskipSeekBar = findViewById(R.id.frameskipSeekBar); frameskipSeekBar.setOnSeekBarChangeListener(this); frameskipSeekBar.setProgress(defaultFrameskip); - ((TextView)(findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip)); + ((TextView) (findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip)); this.updateStats(); } - public static float round(float number, int digits) { - float div = (float) Math.pow(10.0f, digits); - return Math.round(number*div)/div; - } - + /** + * Updates the statistics view. + */ public void updateStats() { final Track trackRef = this.track; - final TextView lastAvgView = (TextView) findViewById(R.id.avgVeloLastView); - final TextView overallAvgView = (TextView) findViewById(R.id.avgVeloOverallView); - final TextView delayedAvgView = (TextView) findViewById(R.id.delayedAvgTextView); - final TextView stepsView = (TextView) findViewById(R.id.stepsTextView); + final TextView lastAvgView = findViewById(R.id.avgVeloLastView); + final TextView overallAvgView = findViewById(R.id.avgVeloOverallView); + final TextView delayedAvgView = findViewById(R.id.delayedAvgTextView); + final TextView stepsView = findViewById(R.id.stepsTextView); runOnUiThread(new Runnable() { @Override public void run() { @@ -93,46 +155,59 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar }); } + /** + * Handler function for clicks on the "Step" button. + * + * @param view the view the event is generated from + */ public void onStepButtonClick(View view) { this.track.timeElapse(); this.updateStats(); this.renderer.draw(); } + /** + * Handler function for clicks on the "Play" button. + * + * @param view the view the event is generated from + */ public void onPlayButtonClick(View view) { - Button playButton = (Button) findViewById(R.id.playButton); + Button playButton = findViewById(R.id.playButton); playButton.setEnabled(false); - Button stepButton = (Button) findViewById(R.id.stepButton); + Button stepButton = findViewById(R.id.stepButton); stepButton.setEnabled(false); - Button stopButton = (Button) findViewById(R.id.stopButton); + Button stopButton = findViewById(R.id.stopButton); stopButton.setEnabled(true); - Button clearButton = (Button) findViewById(R.id.clearButton); + Button clearButton = findViewById(R.id.clearButton); clearButton.setEnabled(false); - int frameskip = ((SeekBar)(findViewById(R.id.frameskipSeekBar))).getProgress(); + int frameskip = ((SeekBar) (findViewById(R.id.frameskipSeekBar))).getProgress(); this.worker = new Worker(track, this, renderer, frameskip); this.worker.start(); } + /** + * Handler function for clicks on the "Stop" button. + * + * @param view the view the event is generated from + */ public void onStopButtonClick(View view) { - Button playButton = (Button) findViewById(R.id.playButton); + Button playButton = findViewById(R.id.playButton); playButton.setEnabled(true); - Button stepButton = (Button) findViewById(R.id.stepButton); + Button stepButton = findViewById(R.id.stepButton); stepButton.setEnabled(true); - Button stopButton = (Button) findViewById(R.id.stopButton); + Button stopButton = findViewById(R.id.stopButton); stopButton.setEnabled(false); - Button clearButton = (Button) findViewById(R.id.clearButton); + Button clearButton = findViewById(R.id.clearButton); clearButton.setEnabled(true); - this.worker.setStop(true); - try { - this.worker.join(); - } catch (InterruptedException ex) { - - } + this.stopWorker(); this.worker = null; } + /** + * Stops the current worker thread. + */ protected void stopWorker() { this.worker.setStop(true); try { @@ -142,15 +217,20 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar this.worker = null; } + /** + * Handler function for clicks on the "Stop" button. + * + * @param view the view the event is generated from + */ public void onClearButtonClick(View view) { if (this.worker != null) { // There was a simulation running this.stopWorker(); } - Button playButton = (Button) findViewById(R.id.playButton); + Button playButton = findViewById(R.id.playButton); playButton.setEnabled(true); - Button stepButton = (Button) findViewById(R.id.stepButton); + Button stepButton = findViewById(R.id.stepButton); stepButton.setEnabled(true); - Button stopButton = (Button) findViewById(R.id.stopButton); + Button stopButton = findViewById(R.id.stopButton); stopButton.setEnabled(false); this.updateTrack(); this.updateStats(); @@ -158,24 +238,28 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar } + /** + * Creates a new track with the current settings. This methods restarts the simulation it + * it was running before. + */ protected void updateTrack() { - int newTrackLength = ((SeekBar)(findViewById(R.id.trackLengthSeekBar))).getProgress(); - SeekBar noOfVehiclesSeekBar = (SeekBar)(findViewById(R.id.noOfVehiclesSeekBar)); + int newTrackLength = ((SeekBar) (findViewById(R.id.trackLengthSeekBar))).getProgress(); + SeekBar noOfVehiclesSeekBar = findViewById(R.id.noOfVehiclesSeekBar); int newNoOfVehicles = noOfVehiclesSeekBar.getProgress(); if (newTrackLength < newNoOfVehicles) { // Dont allow values greater than track length! - newNoOfVehicles = (int) newTrackLength; + newNoOfVehicles = newTrackLength; noOfVehiclesSeekBar.setProgress(newNoOfVehicles); } - TextView noOfVehiclesTextView = (TextView)(findViewById(R.id.noOfVehiclesTextView)); + TextView noOfVehiclesTextView = findViewById(R.id.noOfVehiclesTextView); noOfVehiclesTextView.setText(String.valueOf(newNoOfVehicles)); - TextView trackLengthTextView = (TextView)(findViewById(R.id.trackLengthTextView)); + TextView trackLengthTextView = findViewById(R.id.trackLengthTextView); trackLengthTextView.setText(String.valueOf(newTrackLength)); float newMaxVelocity = ((SeekBar) findViewById(R.id.maxVelocitySeekBar)).getProgress(); - SeekBar brakeProbabilitySeekBar = (SeekBar) findViewById(R.id.brakeProbabilitySeekBar); - float newBrakeProb = (float)brakeProbabilitySeekBar.getProgress() + SeekBar brakeProbabilitySeekBar = findViewById(R.id.brakeProbabilitySeekBar); + float newBrakeProb = (float) brakeProbabilitySeekBar.getProgress() / (float)brakeProbabilitySeekBar.getMax(); int newDelay = ((SeekBar)(findViewById(R.id.simDelaySeekBar))).getProgress(); @@ -186,7 +270,8 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar this.worker.setStop(true); try { this.worker.join(); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + } } this.track = new Track(newNoOfVehicles, newTrackLength, newBrakeProb, newMaxVelocity, @@ -199,27 +284,34 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar } } + /** + * Handler function for changes on the seek bars. + * + * @param seekBar the seek bar changed + * @param progress the new progress value of the seek bar + * @param fromUser flag if the event is the result of an user action + */ @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (seekBar == (SeekBar)(findViewById(R.id.noOfVehiclesSeekBar))) { + if (seekBar == findViewById(R.id.noOfVehiclesSeekBar)) { this.updateTrack(); - } else if (seekBar == (SeekBar)(findViewById(R.id.brakeProbabilitySeekBar))) { - float newBrakeProb = (float)seekBar.getProgress() / (float)seekBar.getMax(); + } else if (seekBar == findViewById(R.id.brakeProbabilitySeekBar)) { + float newBrakeProb = (float) seekBar.getProgress() / (float) seekBar.getMax(); this.track.setBrakeProb(newBrakeProb); - TextView newBrakeProbTextView = (TextView)(findViewById(R.id.brakeProbTextView)); + TextView newBrakeProbTextView = findViewById(R.id.brakeProbTextView); newBrakeProbTextView.setText(String.valueOf((newBrakeProb))); - } else if (seekBar == (SeekBar)(findViewById(R.id.maxVelocitySeekBar))) { + } else if (seekBar == findViewById(R.id.maxVelocitySeekBar)) { this.track.setMaxVelocity(seekBar.getProgress()); - TextView tv = (TextView)(findViewById(R.id.maxVeloTextView)); + TextView tv = findViewById(R.id.maxVeloTextView); tv.setText(String.valueOf(progress)); - } else if (seekBar == (SeekBar)(findViewById(R.id.trackLengthSeekBar))) { + } else if (seekBar == findViewById(R.id.trackLengthSeekBar)) { this.updateTrack(); - } else if (seekBar == (SeekBar)(findViewById(R.id.simDelaySeekBar))) { - TextView tv = (TextView)(findViewById(R.id.simDelayTextView)); + } else if (seekBar == findViewById(R.id.simDelaySeekBar)) { + TextView tv = findViewById(R.id.simDelayTextView); tv.setText(String.valueOf(seekBar.getProgress())); this.track.setWaitTime(seekBar.getProgress()); - } else if (seekBar == (SeekBar)(findViewById(R.id.frameskipSeekBar))) { - TextView tv = (TextView)(findViewById(R.id.frameSkipTextView)); + } else if (seekBar == findViewById(R.id.frameskipSeekBar)) { + TextView tv = findViewById(R.id.frameSkipTextView); tv.setText(String.valueOf(seekBar.getProgress())); if (this.worker != null) { this.worker.setFrameskip(seekBar.getProgress()); @@ -227,11 +319,21 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar } } + /** + * Handler function for beginning touch events on a seek bar. + * + * @param seekBar the seek bar touched + */ @Override public void onStartTrackingTouch(SeekBar seekBar) { } + /** + * Handler function for ending touch events on a seek bar. + * + * @param seekBar the seek bar touched + */ @Override public void onStopTrackingTouch(SeekBar seekBar) { diff --git a/app/src/main/java/de/hems/trafficsim/Renderer.java b/app/src/main/java/de/hems/trafficsim/Renderer.java index 14b1bf8..06787d3 100644 --- a/app/src/main/java/de/hems/trafficsim/Renderer.java +++ b/app/src/main/java/de/hems/trafficsim/Renderer.java @@ -9,25 +9,73 @@ import android.view.SurfaceHolder; import java.util.ConcurrentModificationException; import java.util.List; +/** + * User interface class rendering the track history on a SurfaceView. + */ public class Renderer { - private List stepRecords; + /** + * the track to render + */ protected Track track; + /** + * width of a rectangle representing one vehicle + */ protected int pixelPerVehicle; + /** + * height of a rectangle representing one vehicle + */ protected int pixelPerLine; + /** + * amount of pixels per track position which are lost by rounding + */ protected float tooShortPerTrackLength; + /** + * amount of pixels per height which are lost by rounding + */ protected float tooShortPerHeight; + /** + * Paint instance of the renderer + */ protected Paint paint; + /** + * holder of the surface the renderer draws to + */ protected SurfaceHolder holder; - protected Canvas canvas; + /** + * width of the surface to draw to + */ protected int width; + /** + * height of the surface to draw to + */ protected int height; + /** + * temporary reference to the time records of one simulation step + */ + private List stepRecords; + /** + * temporary reference to the canvas to draw to + */ + private Canvas canvas; + /** + * Constuctor for a Renderer. + * + * @param track the track to render + * @param holder the holder of the surface to draw to + */ public Renderer(Track track, SurfaceHolder holder) { this.track = track; this.holder = holder; this.paint = new Paint(); } + /** + * Updates the dimension information of the renderer. + * + * @param width the width of the SurfaceView + * @param height the height of the SurfaceView + */ public void setSize(int width, int height) { if (width > 0 && height > 0) { this.pixelPerVehicle = width / (int) this.track.getTrackLength(); @@ -36,34 +84,50 @@ public class Renderer { / this.track.getTrackLength(); this.pixelPerLine = height / this.track.getHistoryLength(); this.tooShortPerHeight = (height - this.pixelPerLine * this.track.getHistoryLength()) - / (float)height; - System.out.println("Viewport: "+width+"x"+height); + / (float) height; + System.out.println("Viewport: " + width + "x" + height); this.width = width; this.height = height; } } + /** + * Updates the track of the Surface. + * + * @param track the new track to render + */ public void setTrack(Track track) { this.track = track; this.setSize(width, height); } + /** + * Utility function which calculates a color from the relation between the current speed of a + * vehicle and it's maximum speed (from red over yellow to green). + * + * @param curVelocity current velocity of the vehicle + * @param maxVelocity maximum velocity of the vehilce + * @return color encoded as Android color integer + */ protected int getColor(float curVelocity, float maxVelocity) { float perc = curVelocity / maxVelocity; perc = 1 - perc; if (perc <= 0.5) { - int red = ((int) (2*perc*0xFF)) << 16; + int red = ((int) (2 * perc * 0xFF)) << 16; int green = 0xFF << 8; int blue = 0; return 0xff000000 | red | green | blue; } else { int red = 0xFF << 16; - int green = ((int)(0xFF-0xFF*(perc-0.5)*2)) << 8; + int green = ((int) (0xFF - 0xFF * (perc - 0.5) * 2)) << 8; int blue = 0; return 0xff000000 | red | green | blue; } } + /** + * Draws the current state of the track history to the Surface. + */ protected void draw() { try { if (this.pixelPerVehicle == 0) { @@ -79,7 +143,7 @@ public class Renderer { int vCompensateStart = 0; int vCompensateEnd = 0; canvas.drawColor(Color.BLACK); - for (int curStepIdx = this.track.getVtrList().size()-1;curStepIdx >= 0; + for (int curStepIdx = this.track.getVtrList().size()-1; curStepIdx >= 0; curStepIdx--) { try { try { diff --git a/app/src/main/java/de/hems/trafficsim/TimeRecordView.java b/app/src/main/java/de/hems/trafficsim/TimeRecordView.java index da9aedd..4b3ee7d 100644 --- a/app/src/main/java/de/hems/trafficsim/TimeRecordView.java +++ b/app/src/main/java/de/hems/trafficsim/TimeRecordView.java @@ -1,24 +1,26 @@ package de.hems.trafficsim; -import android.app.ActionBar; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.text.Layout; import android.view.SurfaceView; -import android.view.View; import android.view.ViewGroup; -import java.util.ConcurrentModificationException; -import java.util.List; - +/** + * A Surface View which servers as a stage for a renderer. + */ public class TimeRecordView extends SurfaceView { public TimeRecordView(Context context, Track track) { super(context); this.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } + /** + * Updates the size information of the SurfaceView, used by the Android SDK. + * + * @param w new width + * @param h new height + * @param oldw old width + * @param oldh old height + */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); diff --git a/app/src/main/java/de/hems/trafficsim/Track.java b/app/src/main/java/de/hems/trafficsim/Track.java index d372319..2f139b0 100644 --- a/app/src/main/java/de/hems/trafficsim/Track.java +++ b/app/src/main/java/de/hems/trafficsim/Track.java @@ -5,42 +5,92 @@ import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.LockSupport; +/** + * Main model class of TrafficSim. Represents a round course containing vehicles. + */ public class Track extends Observable { + /** + * list a vehicles on the track + */ protected List vehicles; + + /** + * list of resulting time records of the simulation + */ protected List> vtrList; + + /** + * length of the track + */ protected float trackLength; + + /** + * sum of all vehicle speeds during the simulation, used for average calculation + */ protected float sumAvgMemory; + + /** + * sum of all vehicle speeds during the simulation ignoring the first ten steps, + * used for average calculation + */ protected float sumDelAvgMemory; + + /** + * length of the history kept + */ protected int historyLength; + + /** + * average over all velocities in the simulation + */ protected float overallAvg; + + /** + * average over all velocities in the simulation, ignoring the first ten steps + */ protected float delayedAvg; + + /** + * average over all velocities in the last step + */ protected float lastAvg; + + /** + * current configured wait time between two simulation steps + */ protected int waitTime; + + /** + * currently configured max velocity for all vehicles + */ protected float maxVelocity; + + /** + * currently configured brake probability for all vehicles + */ protected float brakeProb; + + /** + * counter for executed simulation steps + */ protected long steps; - protected Semaphore listSemaphore; - public List> getVtrList() { return vtrList; } - public float getOverallAvg() { - return overallAvg; - } - public float getLastAvg() { - return lastAvg; - } - public float getDelayedAvg() { return delayedAvg; } - public List getVehicles() { - return vehicles; - } - public float getTrackLength() { - return trackLength; - } - public long getSteps() { return steps; } - public Semaphore getListSemaphore() { return listSemaphore; } - public int getHistoryLength() { return historyLength; } + /** + * semaphore protecting the vtrlist + */ + protected Semaphore listSemaphore; + /** + * Constructor for a new Track. + * + * @param numberVehicles number of vehicles on the Track + * @param trackLength length of the new Track + * @param brakeProb probability of a vehicle to suddenly brake without reason + * @param maxVelocity maxmimum velocity of the vehicles + * @param waitTime time between two simulation steps to slow down the simulation artificially + * @param historyLength length of the history to keep + */ public Track(int numberVehicles, float trackLength, float brakeProb, float maxVelocity, int waitTime, int historyLength) { this.trackLength = trackLength; this.brakeProb = brakeProb; @@ -59,9 +109,96 @@ public class Track extends Observable { } - protected List createVehiclesList(int numberVehicles){ + /** + * Getter for vtrList + * + * @return vtrList + */ + public List> getVtrList() { + return vtrList; + } + + /** + * Getter for overallAvg + * + * @return overallAvg + */ + public float getOverallAvg() { + return overallAvg; + } + + /** + * Getter for lastAvg + * + * @return lastAvg + */ + public float getLastAvg() { + return lastAvg; + } + + /** + * Getter for delayedAvg + * + * @return delayedAvg + */ + public float getDelayedAvg() { + return delayedAvg; + } + + /** + * Getter for vehicles + * + * @return vehicles + */ + public List getVehicles() { + return vehicles; + } + + /** + * Getter for trackLength + * + * @return trackLength + */ + public float getTrackLength() { + return trackLength; + } + + /** + * Getter for steps + * + * @return steps + */ + public long getSteps() { + return steps; + } + + /** + * Getter for listSemaphore + * + * @return listSemaphore + */ + public Semaphore getListSemaphore() { + return listSemaphore; + } + + /** + * Getter for historyLength + * + * @return historyLength + */ + public int getHistoryLength() { + return historyLength; + } + + /** + * Utility function to add vehicles to the Track. + * + * @param numberVehicles number of vehicles to add + * @return filled list with vehicles + */ + protected List createVehiclesList(int numberVehicles) { List result = new ArrayList<>(); - for(int i=0;i= vehicles.size()) { @@ -94,7 +249,7 @@ public class Track extends Observable { } Vehicle forerunner = vehicles.get(forerunnerIndex); float distanceForerunner = forerunner.getPosition() - v.getPosition() - 1; - if(distanceForerunner < 0.0){ + if (distanceForerunner < 0.0) { distanceForerunner += this.trackLength; } v.updateVelocity(distanceForerunner); @@ -117,25 +272,32 @@ public class Track extends Observable { this.listSemaphore.release(); update_avg(); - this.setChanged(); - //this.notifyObservers(); - this.clearChanged(); - //LockSupport.parkNanos(1); - try { + try { Thread.sleep(waitTime); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + } } - public float avg_step(int step){ + /** + * Returns the average velocity of the given simulation step. + * + * @param step index of the step in the history list + * @return average velocity + */ + public float avg_step(int step) { float sum_step = 0; for (VehicleTimeRecord r : vtrList.get(step)) { - sum_step += r.velocity; + sum_step += r.velocity; } return sum_step / vehicles.size(); } - protected void update_avg(){ - lastAvg = avg_step(vtrList.size()-1); + /** + * Utility function which updates the averages values with the results from the last simulation + * step. + */ + protected void update_avg() { + lastAvg = avg_step(vtrList.size() - 1); if (this.steps > 10) { sumDelAvgMemory += lastAvg; delayedAvg = sumDelAvgMemory / (this.steps - 10); @@ -144,13 +306,4 @@ public class Track extends Observable { overallAvg = sumAvgMemory / this.steps; } - - public float avg_span(int start, int end){ - float sum_span = 0; - for (int i=start; i + *
  • Increase by one if the vehicle is slower than it's maximum speed
  • + *
  • Random brake by one
  • + *
  • Break if the distance to the forerunner is less than it's speed
  • + * + * + * @param distanceForerunner distance to the forerunner of the vehicle + */ public void updateVelocity(float distanceForerunner) { Random random = new Random(); @@ -53,6 +126,9 @@ public class Vehicle { } } + /** + * Updates the position of the vehicle according to it's previously calculated speed. + */ public void timeElapse() { position = (position + curVelocity) % this.trackLength; } diff --git a/app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java b/app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java index 339a9bf..2da5374 100644 --- a/app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java +++ b/app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java @@ -1,9 +1,27 @@ package de.hems.trafficsim; +/** + * Model class which keeps the previous simulation data for analysis. + */ public class VehicleTimeRecord { + /** + * the id of the vehicle it belongs to + */ protected int id; + + /** + * the position of the vehicle after the last simulation step + */ protected float position; + + /** + * the velocity of the vehicle during the last simulation step + */ protected float velocity; + + /** + * the maximum velocity of the vehicle + */ protected float maxVelocity; public float getMaxVelocity() { @@ -18,6 +36,14 @@ public class VehicleTimeRecord { return velocity; } + /** + * Construct a new VehicleTimeRecord. + * + * @param id the id of the vehicle it belongs to + * @param position the position of the vehicle after the last simulation step + * @param velocity the velocity of the vehicle during the last simulation step + * @param maxVelocity the maximum velocity of the vehicle + */ public VehicleTimeRecord(int id, float position, float velocity, float maxVelocity) { this.id = id; this.position = position; diff --git a/app/src/main/java/de/hems/trafficsim/Worker.java b/app/src/main/java/de/hems/trafficsim/Worker.java index 89fdcf8..942e93b 100644 --- a/app/src/main/java/de/hems/trafficsim/Worker.java +++ b/app/src/main/java/de/hems/trafficsim/Worker.java @@ -1,17 +1,46 @@ package de.hems.trafficsim; import android.graphics.Canvas; -import android.view.SurfaceHolder; - -import java.util.ConcurrentModificationException; +/** + * Calculation thread, which runs the simulation itself and also updates the view with a set rate. + */ public class Worker extends Thread { + /** + * the track to simulate + */ protected Track track; + + /** + * Stop flag, indicating that the worker shall stop + */ protected boolean stop; + + /** + * the MainActivity of the gui + */ protected MainActivity gui; + + /** + * the Renderer drawing the Track + */ protected Renderer renderer; + + /** + * Amount of simulation steps between two frames in the view. Zero means + * that every simulation step is drawn. + */ protected int frameskip; + /** + * Construct a new worker. + * + * @param track the track to simulate + * @param gui the MainActivity of the gui + * @param renderer the Renderer drawing the Track + * @param frameskip Amount of simulation steps between two frames in the view. Zero means + * that every simulation step is drawn + */ public Worker(Track track, MainActivity gui, Renderer renderer, int frameskip) { super(); this.track = track; @@ -21,16 +50,28 @@ public class Worker extends Thread { this.frameskip = frameskip; } + /** + * Sets a flag that causes the worker to stop. + * + * @param stop flag whether the thread shall stop + */ public void setStop(boolean stop) { this.stop = stop; } + /** + * Updates the amount of simulation steps between to view frames. + * + * @param frames amount of steps + */ public void setFrameskip(int frames) { this.frameskip = frames; } - public int getFrameskip() { return this.frameskip; } - + /** + * Method which is automatically executed when the thread is started. It alternates between + * updating the simulation and drawing a new frame in the view. + */ @Override public void run() { Canvas canvas = null; @@ -40,7 +81,7 @@ public class Worker extends Thread { this.gui.updateStats(); if (i >= this.frameskip) { this.renderer.draw(); - i=0; + i = 0; } i++; } diff --git a/app/src/test/java/de/hems/trafficsim/ExampleUnitTest.java b/app/src/test/java/de/hems/trafficsim/ExampleUnitTest.java deleted file mode 100644 index 0b68bb6..0000000 --- a/app/src/test/java/de/hems/trafficsim/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.hems.trafficsim; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file