Browse Source

Add javadoc, remove empty tests

tags/Release_1
Loch Christian (uib05376) Chris <chris@skalarprodukt.de> 3 years ago
parent
commit
ada569035d
11 changed files with 600 additions and 170 deletions
  1. +6
    -0
      .idea/compiler.xml
  2. +4
    -1
      .idea/misc.xml
  3. +0
    -26
      app/src/androidTest/java/de/hems/trafficsim/ExampleInstrumentedTest.java
  4. +161
    -59
      app/src/main/java/de/hems/trafficsim/MainActivity.java
  5. +71
    -7
      app/src/main/java/de/hems/trafficsim/Renderer.java
  6. +11
    -9
      app/src/main/java/de/hems/trafficsim/TimeRecordView.java
  7. +195
    -42
      app/src/main/java/de/hems/trafficsim/Track.java
  8. +79
    -3
      app/src/main/java/de/hems/trafficsim/Vehicle.java
  9. +26
    -0
      app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java
  10. +47
    -6
      app/src/main/java/de/hems/trafficsim/Worker.java
  11. +0
    -17
      app/src/test/java/de/hems/trafficsim/ExampleUnitTest.java

+ 6
- 0
.idea/compiler.xml View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

+ 4
- 1
.idea/misc.xml View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" value="$USER_HOME$/Desktop/TrafficSimDoc" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">


+ 0
- 26
app/src/androidTest/java/de/hems/trafficsim/ExampleInstrumentedTest.java View File

@@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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());
}
}

+ 161
- 59
app/src/main/java/de/hems/trafficsim/MainActivity.java View File

@@ -1,7 +1,5 @@
package de.hems.trafficsim; package de.hems.trafficsim;


import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@@ -9,79 +7,143 @@ import android.widget.LinearLayout;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; 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 { public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
/**
* default value of the number of vehicles on the track
*/
public static final int defaultNoOfVehicles = 25; public static final int defaultNoOfVehicles = 25;

/**
* default value of the length of the track
*/
public static final int defaultTrackLength = 100; public static final int defaultTrackLength = 100;

/**
* default value of brake probability
*/
public static final float defaultBrakeProb = 0.3f; public static final float defaultBrakeProb = 0.3f;

/**
* default value of the maximum velocity of the vehicles
*/
public static final float defaultMaxVelocity = 5.0f; public static final float defaultMaxVelocity = 5.0f;

/**
* default value of the delay between two simulation steps
*/
public static final int defaultDelay = 0; public static final int defaultDelay = 0;

/**
* default value of the number of vehicles on the track
*/
public static final int defaultHistoryLength = 50; public static final int defaultHistoryLength = 50;

/**
* default value of the number of vehicles on the track
*/
public static final int defaultFrameskip = 0; public static final int defaultFrameskip = 0;

/**
* the track to show in the activity
*/
protected Track track; protected Track track;

/**
* the surface view on which the renderer draws the track history
*/
protected TimeRecordView trackView; protected TimeRecordView trackView;

/**
* the thread which runs the simulation and visualization
*/
protected Worker worker; protected Worker worker;

/**
* the renderer instance drawing the track history
*/
protected Renderer renderer; protected Renderer renderer;

/**
* the layout which keeps the surface view
*/
protected LinearLayout viewStack; 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
this.track = new Track(defaultNoOfVehicles, defaultTrackLength, defaultBrakeProb, this.track = new Track(defaultNoOfVehicles, defaultTrackLength, defaultBrakeProb,
defaultMaxVelocity, defaultDelay, defaultHistoryLength); defaultMaxVelocity, defaultDelay, defaultHistoryLength);
this.viewStack = (LinearLayout) findViewById(R.id.trackViewStack);
this.viewStack = findViewById(R.id.trackViewStack);
this.trackView = new TimeRecordView(this, track); this.trackView = new TimeRecordView(this, track);


viewStack.addView(this.trackView); viewStack.addView(this.trackView);
this.renderer = new Renderer(track, this.trackView.getHolder()); 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.setOnSeekBarChangeListener(this);
trackLengthSeekBar.setProgress(defaultTrackLength); trackLengthSeekBar.setProgress(defaultTrackLength);
((TextView)(findViewById(R.id.trackLengthTextView))).setText(String.valueOf(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.setOnSeekBarChangeListener(this);
maxVelocitySeekBar.setProgress((int)defaultMaxVelocity); maxVelocitySeekBar.setProgress((int)defaultMaxVelocity);
((TextView)(findViewById(R.id.maxVeloTextView))).setText(String.valueOf((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.setOnSeekBarChangeListener(this);
noOfVehiclesSeekBar.setProgress(defaultNoOfVehicles); noOfVehiclesSeekBar.setProgress(defaultNoOfVehicles);
((TextView)(findViewById(R.id.noOfVehiclesTextView))).setText(String.valueOf(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.setOnSeekBarChangeListener(this);
brakeProbabilitySeekBar.setProgress((int)(defaultBrakeProb*20)); brakeProbabilitySeekBar.setProgress((int)(defaultBrakeProb*20));
((TextView)(findViewById(R.id.brakeProbTextView))).setText(String.valueOf(defaultBrakeProb)); ((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.setOnSeekBarChangeListener(this);
delaySeekBar.setProgress(defaultDelay); 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.setOnSeekBarChangeListener(this);
frameskipSeekBar.setProgress(defaultFrameskip); frameskipSeekBar.setProgress(defaultFrameskip);
((TextView)(findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip));
((TextView) (findViewById(R.id.frameSkipTextView))).setText(String.valueOf(defaultFrameskip));


this.updateStats(); 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() { public void updateStats() {
final Track trackRef = this.track; 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() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { 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) { public void onStepButtonClick(View view) {
this.track.timeElapse(); this.track.timeElapse();
this.updateStats(); this.updateStats();
this.renderer.draw(); 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) { public void onPlayButtonClick(View view) {
Button playButton = (Button) findViewById(R.id.playButton);
Button playButton = findViewById(R.id.playButton);
playButton.setEnabled(false); playButton.setEnabled(false);
Button stepButton = (Button) findViewById(R.id.stepButton);
Button stepButton = findViewById(R.id.stepButton);
stepButton.setEnabled(false); stepButton.setEnabled(false);
Button stopButton = (Button) findViewById(R.id.stopButton);
Button stopButton = findViewById(R.id.stopButton);
stopButton.setEnabled(true); stopButton.setEnabled(true);
Button clearButton = (Button) findViewById(R.id.clearButton);
Button clearButton = findViewById(R.id.clearButton);
clearButton.setEnabled(false); 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 = new Worker(track, this, renderer, frameskip);
this.worker.start(); 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) { public void onStopButtonClick(View view) {
Button playButton = (Button) findViewById(R.id.playButton);
Button playButton = findViewById(R.id.playButton);
playButton.setEnabled(true); playButton.setEnabled(true);
Button stepButton = (Button) findViewById(R.id.stepButton);
Button stepButton = findViewById(R.id.stepButton);
stepButton.setEnabled(true); stepButton.setEnabled(true);
Button stopButton = (Button) findViewById(R.id.stopButton);
Button stopButton = findViewById(R.id.stopButton);
stopButton.setEnabled(false); stopButton.setEnabled(false);
Button clearButton = (Button) findViewById(R.id.clearButton);
Button clearButton = findViewById(R.id.clearButton);
clearButton.setEnabled(true); clearButton.setEnabled(true);


this.worker.setStop(true);
try {
this.worker.join();
} catch (InterruptedException ex) {

}
this.stopWorker();
this.worker = null; this.worker = null;
} }


/**
* Stops the current worker thread.
*/
protected void stopWorker() { protected void stopWorker() {
this.worker.setStop(true); this.worker.setStop(true);
try { try {
@@ -142,15 +217,20 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar
this.worker = null; 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) { public void onClearButtonClick(View view) {
if (this.worker != null) { // There was a simulation running if (this.worker != null) { // There was a simulation running
this.stopWorker(); this.stopWorker();
} }
Button playButton = (Button) findViewById(R.id.playButton);
Button playButton = findViewById(R.id.playButton);
playButton.setEnabled(true); playButton.setEnabled(true);
Button stepButton = (Button) findViewById(R.id.stepButton);
Button stepButton = findViewById(R.id.stepButton);
stepButton.setEnabled(true); stepButton.setEnabled(true);
Button stopButton = (Button) findViewById(R.id.stopButton);
Button stopButton = findViewById(R.id.stopButton);
stopButton.setEnabled(false); stopButton.setEnabled(false);
this.updateTrack(); this.updateTrack();
this.updateStats(); 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() { 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(); int newNoOfVehicles = noOfVehiclesSeekBar.getProgress();
if (newTrackLength < newNoOfVehicles) { // Dont allow values greater than track length! if (newTrackLength < newNoOfVehicles) { // Dont allow values greater than track length!
newNoOfVehicles = (int) newTrackLength;
newNoOfVehicles = newTrackLength;
noOfVehiclesSeekBar.setProgress(newNoOfVehicles); noOfVehiclesSeekBar.setProgress(newNoOfVehicles);


} }
TextView noOfVehiclesTextView = (TextView)(findViewById(R.id.noOfVehiclesTextView));
TextView noOfVehiclesTextView = findViewById(R.id.noOfVehiclesTextView);
noOfVehiclesTextView.setText(String.valueOf(newNoOfVehicles)); noOfVehiclesTextView.setText(String.valueOf(newNoOfVehicles));


TextView trackLengthTextView = (TextView)(findViewById(R.id.trackLengthTextView));
TextView trackLengthTextView = findViewById(R.id.trackLengthTextView);
trackLengthTextView.setText(String.valueOf(newTrackLength)); trackLengthTextView.setText(String.valueOf(newTrackLength));


float newMaxVelocity = ((SeekBar) findViewById(R.id.maxVelocitySeekBar)).getProgress(); 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(); / (float)brakeProbabilitySeekBar.getMax();


int newDelay = ((SeekBar)(findViewById(R.id.simDelaySeekBar))).getProgress(); int newDelay = ((SeekBar)(findViewById(R.id.simDelaySeekBar))).getProgress();
@@ -186,7 +270,8 @@ public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBar
this.worker.setStop(true); this.worker.setStop(true);
try { try {
this.worker.join(); this.worker.join();
} catch (InterruptedException ex) { }
} catch (InterruptedException ex) {
}
} }


this.track = new Track(newNoOfVehicles, newTrackLength, newBrakeProb, newMaxVelocity, 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 @Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (seekBar == (SeekBar)(findViewById(R.id.noOfVehiclesSeekBar))) {
if (seekBar == findViewById(R.id.noOfVehiclesSeekBar)) {
this.updateTrack(); 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); this.track.setBrakeProb(newBrakeProb);
TextView newBrakeProbTextView = (TextView)(findViewById(R.id.brakeProbTextView));
TextView newBrakeProbTextView = findViewById(R.id.brakeProbTextView);
newBrakeProbTextView.setText(String.valueOf((newBrakeProb))); 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()); this.track.setMaxVelocity(seekBar.getProgress());
TextView tv = (TextView)(findViewById(R.id.maxVeloTextView));
TextView tv = findViewById(R.id.maxVeloTextView);
tv.setText(String.valueOf(progress)); tv.setText(String.valueOf(progress));
} else if (seekBar == (SeekBar)(findViewById(R.id.trackLengthSeekBar))) {
} else if (seekBar == findViewById(R.id.trackLengthSeekBar)) {
this.updateTrack(); 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())); tv.setText(String.valueOf(seekBar.getProgress()));
this.track.setWaitTime(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())); tv.setText(String.valueOf(seekBar.getProgress()));
if (this.worker != null) { if (this.worker != null) {
this.worker.setFrameskip(seekBar.getProgress()); 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 @Override
public void onStartTrackingTouch(SeekBar seekBar) { public void onStartTrackingTouch(SeekBar seekBar) {


} }


/**
* Handler function for ending touch events on a seek bar.
*
* @param seekBar the seek bar touched
*/
@Override @Override
public void onStopTrackingTouch(SeekBar seekBar) { public void onStopTrackingTouch(SeekBar seekBar) {




+ 71
- 7
app/src/main/java/de/hems/trafficsim/Renderer.java View File

@@ -9,25 +9,73 @@ import android.view.SurfaceHolder;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.List; import java.util.List;


/**
* User interface class rendering the track history on a SurfaceView.
*/
public class Renderer { public class Renderer {
private List<VehicleTimeRecord> stepRecords;
/**
* the track to render
*/
protected Track track; protected Track track;
/**
* width of a rectangle representing one vehicle
*/
protected int pixelPerVehicle; protected int pixelPerVehicle;
/**
* height of a rectangle representing one vehicle
*/
protected int pixelPerLine; protected int pixelPerLine;
/**
* amount of pixels per track position which are lost by rounding
*/
protected float tooShortPerTrackLength; protected float tooShortPerTrackLength;
/**
* amount of pixels per height which are lost by rounding
*/
protected float tooShortPerHeight; protected float tooShortPerHeight;
/**
* Paint instance of the renderer
*/
protected Paint paint; protected Paint paint;
/**
* holder of the surface the renderer draws to
*/
protected SurfaceHolder holder; protected SurfaceHolder holder;
protected Canvas canvas;
/**
* width of the surface to draw to
*/
protected int width; protected int width;
/**
* height of the surface to draw to
*/
protected int height; protected int height;
/**
* temporary reference to the time records of one simulation step
*/
private List<VehicleTimeRecord> 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) { public Renderer(Track track, SurfaceHolder holder) {
this.track = track; this.track = track;
this.holder = holder; this.holder = holder;
this.paint = new Paint(); 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) { public void setSize(int width, int height) {
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
this.pixelPerVehicle = width / (int) this.track.getTrackLength(); this.pixelPerVehicle = width / (int) this.track.getTrackLength();
@@ -36,34 +84,50 @@ public class Renderer {
/ this.track.getTrackLength(); / this.track.getTrackLength();
this.pixelPerLine = height / this.track.getHistoryLength(); this.pixelPerLine = height / this.track.getHistoryLength();
this.tooShortPerHeight = (height - this.pixelPerLine * 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.width = width;
this.height = height; this.height = height;
} }
} }


/**
* Updates the track of the Surface.
*
* @param track the new track to render
*/
public void setTrack(Track track) { public void setTrack(Track track) {
this.track = track; this.track = track;
this.setSize(width, height); 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) { protected int getColor(float curVelocity, float maxVelocity) {
float perc = curVelocity / maxVelocity; float perc = curVelocity / maxVelocity;
perc = 1 - perc; perc = 1 - perc;
if (perc <= 0.5) { if (perc <= 0.5) {
int red = ((int) (2*perc*0xFF)) << 16;
int red = ((int) (2 * perc * 0xFF)) << 16;
int green = 0xFF << 8; int green = 0xFF << 8;
int blue = 0; int blue = 0;
return 0xff000000 | red | green | blue; return 0xff000000 | red | green | blue;
} else { } else {
int red = 0xFF << 16; 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; int blue = 0;
return 0xff000000 | red | green | blue; return 0xff000000 | red | green | blue;
} }
} }


/**
* Draws the current state of the track history to the Surface.
*/
protected void draw() { protected void draw() {
try { try {
if (this.pixelPerVehicle == 0) { if (this.pixelPerVehicle == 0) {
@@ -79,7 +143,7 @@ public class Renderer {
int vCompensateStart = 0; int vCompensateStart = 0;
int vCompensateEnd = 0; int vCompensateEnd = 0;
canvas.drawColor(Color.BLACK); 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--) { curStepIdx--) {
try { try {
try { try {


+ 11
- 9
app/src/main/java/de/hems/trafficsim/TimeRecordView.java View File

@@ -1,24 +1,26 @@
package de.hems.trafficsim; package de.hems.trafficsim;


import android.app.ActionBar;
import android.content.Context; 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.SurfaceView;
import android.view.View;
import android.view.ViewGroup; 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 class TimeRecordView extends SurfaceView {
public TimeRecordView(Context context, Track track) { public TimeRecordView(Context context, Track track) {
super(context); super(context);
this.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 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 @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) { protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); super.onSizeChanged(w, h, oldw, oldh);


+ 195
- 42
app/src/main/java/de/hems/trafficsim/Track.java View File

@@ -5,42 +5,92 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Observable; import java.util.Observable;
import java.util.concurrent.Semaphore; 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 { public class Track extends Observable {
/**
* list a vehicles on the track
*/
protected List<Vehicle> vehicles; protected List<Vehicle> vehicles;

/**
* list of resulting time records of the simulation
*/
protected List<List<VehicleTimeRecord>> vtrList; protected List<List<VehicleTimeRecord>> vtrList;

/**
* length of the track
*/
protected float trackLength; protected float trackLength;

/**
* sum of all vehicle speeds during the simulation, used for average calculation
*/
protected float sumAvgMemory; protected float sumAvgMemory;

/**
* sum of all vehicle speeds during the simulation ignoring the first ten steps,
* used for average calculation
*/
protected float sumDelAvgMemory; protected float sumDelAvgMemory;

/**
* length of the history kept
*/
protected int historyLength; protected int historyLength;

/**
* average over all velocities in the simulation
*/
protected float overallAvg; protected float overallAvg;

/**
* average over all velocities in the simulation, ignoring the first ten steps
*/
protected float delayedAvg; protected float delayedAvg;

/**
* average over all velocities in the last step
*/
protected float lastAvg; protected float lastAvg;

/**
* current configured wait time between two simulation steps
*/
protected int waitTime; protected int waitTime;

/**
* currently configured max velocity for all vehicles
*/
protected float maxVelocity; protected float maxVelocity;

/**
* currently configured brake probability for all vehicles
*/
protected float brakeProb; protected float brakeProb;

/**
* counter for executed simulation steps
*/
protected long steps; protected long steps;
protected Semaphore listSemaphore;


public List<List<VehicleTimeRecord>> getVtrList() { return vtrList; }
public float getOverallAvg() {
return overallAvg;
}
public float getLastAvg() {
return lastAvg;
}
public float getDelayedAvg() { return delayedAvg; }
public List<Vehicle> 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) { public Track(int numberVehicles, float trackLength, float brakeProb, float maxVelocity, int waitTime, int historyLength) {
this.trackLength = trackLength; this.trackLength = trackLength;
this.brakeProb = brakeProb; this.brakeProb = brakeProb;
@@ -59,9 +109,96 @@ public class Track extends Observable {


} }


protected List<Vehicle> createVehiclesList(int numberVehicles){
/**
* Getter for vtrList
*
* @return vtrList
*/
public List<List<VehicleTimeRecord>> 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<Vehicle> 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<Vehicle> createVehiclesList(int numberVehicles) {
List<Vehicle> result = new ArrayList<>(); List<Vehicle> result = new ArrayList<>();
for(int i=0;i<numberVehicles;i++){
for (int i = 0; i < numberVehicles; i++) {
Vehicle vehicle = new Vehicle(i, i, this.maxVelocity, this.brakeProb, this.trackLength); Vehicle vehicle = new Vehicle(i, i, this.maxVelocity, this.brakeProb, this.trackLength);
result.add(vehicle); result.add(vehicle);
} }
@@ -69,24 +206,42 @@ public class Track extends Observable {
return result; return result;
} }


/**
* Update the wait time of the simulation.
*
* @param waitTime new wait time in ms
*/
public void setWaitTime(int waitTime) { public void setWaitTime(int waitTime) {
this.waitTime = waitTime; this.waitTime = waitTime;
} }


/**
* Update the brake probability of all vehicles.
*
* @param brakeProb new brake probability
*/
public void setBrakeProb(float brakeProb) { public void setBrakeProb(float brakeProb) {
for (Vehicle v : this.vehicles) { for (Vehicle v : this.vehicles) {
v.setBrakeProb(brakeProb); v.setBrakeProb(brakeProb);
} }
} }


/**
* Update the maximum velocity of all vehicles.
*
* @param maxVelocity new maximum velocity
*/
public void setMaxVelocity(float maxVelocity) { public void setMaxVelocity(float maxVelocity) {
for (Vehicle v : this.vehicles) { for (Vehicle v : this.vehicles) {
v.setMaxVelocity(maxVelocity); v.setMaxVelocity(maxVelocity);
} }
} }


/**
* Calculates on simulation step ahead and then waits for the configured wait time.
*/
public void timeElapse() { public void timeElapse() {
for(int i=0; i<vehicles.size();i++) {
for (int i = 0; i < vehicles.size(); i++) {
Vehicle v = vehicles.get(i); Vehicle v = vehicles.get(i);
int forerunnerIndex = i + 1; int forerunnerIndex = i + 1;
if (forerunnerIndex >= vehicles.size()) { if (forerunnerIndex >= vehicles.size()) {
@@ -94,7 +249,7 @@ public class Track extends Observable {
} }
Vehicle forerunner = vehicles.get(forerunnerIndex); Vehicle forerunner = vehicles.get(forerunnerIndex);
float distanceForerunner = forerunner.getPosition() - v.getPosition() - 1; float distanceForerunner = forerunner.getPosition() - v.getPosition() - 1;
if(distanceForerunner < 0.0){
if (distanceForerunner < 0.0) {
distanceForerunner += this.trackLength; distanceForerunner += this.trackLength;
} }
v.updateVelocity(distanceForerunner); v.updateVelocity(distanceForerunner);
@@ -117,25 +272,32 @@ public class Track extends Observable {
this.listSemaphore.release(); this.listSemaphore.release();


update_avg(); update_avg();
this.setChanged();
//this.notifyObservers();
this.clearChanged();
//LockSupport.parkNanos(1);
try {
try {
Thread.sleep(waitTime); 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; float sum_step = 0;
for (VehicleTimeRecord r : vtrList.get(step)) { for (VehicleTimeRecord r : vtrList.get(step)) {
sum_step += r.velocity;
sum_step += r.velocity;
} }
return sum_step / vehicles.size(); 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) { if (this.steps > 10) {
sumDelAvgMemory += lastAvg; sumDelAvgMemory += lastAvg;
delayedAvg = sumDelAvgMemory / (this.steps - 10); delayedAvg = sumDelAvgMemory / (this.steps - 10);
@@ -144,13 +306,4 @@ public class Track extends Observable {
overallAvg = sumAvgMemory / this.steps; overallAvg = sumAvgMemory / this.steps;


} }

public float avg_span(int start, int end){
float sum_span = 0;
for (int i=start; i <end;i++){
sum_span += avg_step(i);
}
return sum_span / (end-start+1);
}

} }

+ 79
- 3
app/src/main/java/de/hems/trafficsim/Vehicle.java View File

@@ -2,15 +2,49 @@ package de.hems.trafficsim;


import java.util.Random; import java.util.Random;


/**
* Model class which represents a vehicle in the simulation.
*/
public class Vehicle { public class Vehicle {
/**
* a number to index the vehicle
*/
protected int id; protected int id;

/**
* current position of the vehicle on the track
*/
protected float position; protected float position;

/**
* current velocity of the vehicle
*/
protected float curVelocity; protected float curVelocity;

/**
* maximum velocity of the vehicle
*/
protected float maxVelocity; protected float maxVelocity;

/**
* probability of the vehicle to brake without reason
*/
protected float brakeProb; protected float brakeProb;
protected float trackLength;


/**
* length of the track the vehicle is placed on
*/
protected float trackLength;


/**
* Constructs a new vehicle.
*
* @param id a number to index the vehicle
* @param position current position of the vehicle on the track
* @param maxVelocity maximum velocity of the vehicle
* @param brakeProb probability of the vehicle to brake without reason
* @param trackLength length of the track the vehicle is placed on
*/
public Vehicle(int id, int position, float maxVelocity, float brakeProb, float trackLength) { public Vehicle(int id, int position, float maxVelocity, float brakeProb, float trackLength) {
this.id = id; this.id = id;
this.position = position; this.position = position;
@@ -21,22 +55,61 @@ public class Vehicle {


} }


/**
* Getter for position
*
* @return position
*/
public float getPosition() { public float getPosition() {
return position; return position;
} }


/**
* Getter for curVelocity
*
* @return curVelocity
*/
public float getCurVelocity() { public float getCurVelocity() {
return curVelocity; return curVelocity;
} }


/**
* Getter for maxVelocity
*
* @return maxVelocity
*/
public float getMaxVelocity() { public float getMaxVelocity() {
return maxVelocity; return maxVelocity;
} }


public void setBrakeProb(float brakeProb) { this.brakeProb = brakeProb; }
/**
* Setter for maxVelocity
*
* @return maxVelocity
*/
public void setMaxVelocity(float maxVelocity) {
this.maxVelocity = maxVelocity;
}


public void setMaxVelocity(float maxVelocity) { this.maxVelocity = maxVelocity; }
/**
* Setter for brakeProb
*
* @return brakeProb
*/
public void setBrakeProb(float brakeProb) {
this.brakeProb = brakeProb;
}


/**
* Calculates the new velocity of the vehicle based on the following rules:
* <ol>
* <li>Increase by one if the vehicle is slower than it's maximum speed</li>
* <li>Random brake by one</li>
* <li>Break if the distance to the forerunner is less than it's speed</li>
* </ol>
*
* @param distanceForerunner distance to the forerunner of the vehicle
*/
public void updateVelocity(float distanceForerunner) { public void updateVelocity(float distanceForerunner) {
Random random = new Random(); 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() { public void timeElapse() {
position = (position + curVelocity) % this.trackLength; position = (position + curVelocity) % this.trackLength;
} }


+ 26
- 0
app/src/main/java/de/hems/trafficsim/VehicleTimeRecord.java View File

@@ -1,9 +1,27 @@
package de.hems.trafficsim; package de.hems.trafficsim;


/**
* Model class which keeps the previous simulation data for analysis.
*/
public class VehicleTimeRecord { public class VehicleTimeRecord {
/**
* the id of the vehicle it belongs to
*/
protected int id; protected int id;

/**
* the position of the vehicle after the last simulation step
*/
protected float position; protected float position;

/**
* the velocity of the vehicle during the last simulation step
*/
protected float velocity; protected float velocity;

/**
* the maximum velocity of the vehicle
*/
protected float maxVelocity; protected float maxVelocity;


public float getMaxVelocity() { public float getMaxVelocity() {
@@ -18,6 +36,14 @@ public class VehicleTimeRecord {
return velocity; 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) { public VehicleTimeRecord(int id, float position, float velocity, float maxVelocity) {
this.id = id; this.id = id;
this.position = position; this.position = position;


+ 47
- 6
app/src/main/java/de/hems/trafficsim/Worker.java View File

@@ -1,17 +1,46 @@
package de.hems.trafficsim; package de.hems.trafficsim;


import android.graphics.Canvas; 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 { public class Worker extends Thread {
/**
* the track to simulate
*/
protected Track track; protected Track track;

/**
* Stop flag, indicating that the worker shall stop
*/
protected boolean stop; protected boolean stop;

/**
* the MainActivity of the gui
*/
protected MainActivity gui; protected MainActivity gui;

/**
* the Renderer drawing the Track
*/
protected Renderer renderer; protected Renderer renderer;

/**
* Amount of simulation steps between two frames in the view. Zero means
* that every simulation step is drawn.
*/
protected int frameskip; 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) { public Worker(Track track, MainActivity gui, Renderer renderer, int frameskip) {
super(); super();
this.track = track; this.track = track;
@@ -21,16 +50,28 @@ public class Worker extends Thread {
this.frameskip = frameskip; 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) { public void setStop(boolean stop) {
this.stop = stop; this.stop = stop;
} }


/**
* Updates the amount of simulation steps between to view frames.
*
* @param frames amount of steps
*/
public void setFrameskip(int frames) { public void setFrameskip(int frames) {
this.frameskip = 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 @Override
public void run() { public void run() {
Canvas canvas = null; Canvas canvas = null;
@@ -40,7 +81,7 @@ public class Worker extends Thread {
this.gui.updateStats(); this.gui.updateStats();
if (i >= this.frameskip) { if (i >= this.frameskip) {
this.renderer.draw(); this.renderer.draw();
i=0;
i = 0;
} }
i++; i++;
} }


+ 0
- 17
app/src/test/java/de/hems/trafficsim/ExampleUnitTest.java View File

@@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

Loading…
Cancel
Save